├── .gitignore ├── .jshintrc ├── README.md ├── assets ├── avatar.xcf ├── blank-map-maker.js ├── bullet.xcf ├── heart.xcf └── screenshot.png ├── index.js ├── package.json └── public ├── audio ├── background.mp3 ├── damage.wav ├── death.wav ├── pickup.wav ├── shoot.wav └── spawn.wav ├── config └── pubnub.json ├── data └── level_0_0.csv ├── images ├── avatar.png ├── bullet.png ├── embed-screenshot.png ├── health.png ├── heart.png └── terrain.png ├── index.html ├── scripts ├── .jshintrc ├── bullets.js ├── enemies.js ├── hud.js ├── libraries │ └── progress.js ├── main.js ├── pickups.js ├── player.js └── world.js └── styles ├── main.css └── progressjs.css /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | assets/blank.csv 3 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "curly": true, 3 | "trailing": true, 4 | "undef": true, 5 | "globalstrict": true, 6 | "node": true, 7 | "devel": true, 8 | "globalstrict": true, 9 | "loopfunc": true 10 | } 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Robot Onslaught 2 | 3 | ![Screenshot](assets/screenshot.png) 4 | 5 | Entry into the [PubNub 2014 Competition](http://pubnubgame.challengepost.com/submissions/28062-robot-onslaught) by Thomas Hunter. 6 | 7 | ## Play The Game 8 | 9 | Visit [thomashunter.name/games/robot-onslaught](https://thomashunter.name/games/robot-onslaught/) using a modern web browser. 10 | 11 | I've only tested this in Chrome and Firefox. 12 | 13 | Use WASD to move and the ARROW keys to shoot. Pro Tip: Map these to a gamepad ;) 14 | 15 | ## Sources 16 | 17 | * Tileset by [Buch@OpenGameArt](http://opengameart.org/content/sci-fi-interior-tiles) 18 | * Uses the [Phaser Game Engine](http://phaser.io/) 19 | * Server built with [Node.js](http://nodejs.org/) 20 | * Progress bar using [Progress.js](http://usablica.github.io/progress.js/) 21 | * And of course, network transport via [PubNub](http://www.pubnub.com/) 22 | * Sound Effects, robot, and Music by me 23 | 24 | ## Code Quality 25 | 26 | I learned Phaser and wrote this from scratch over the course of 10 hours, so the code is a bit ugly. 27 | 28 | ## License 29 | 30 | The code that I've written is GPL, everything else falls under its own respective license. -------------------------------------------------------------------------------- /assets/avatar.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tlhunter/robot-onslaught/e5e73c90bbdc6131912efaa661c5475e412fa39b/assets/avatar.xcf -------------------------------------------------------------------------------- /assets/blank-map-maker.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var fs = require('fs'); 4 | 5 | var map = []; 6 | 7 | var tile_id = process.argv[2] || -1; 8 | 9 | for (var x = 0; x < 100; x++) { 10 | map[x] = []; 11 | for (var y = 0; y < 100; y++) { 12 | map[x][y] = tile_id; 13 | } 14 | } 15 | 16 | var output = ''; 17 | for (var i = 0; i < 100; i++) { 18 | output += map[i].toString(); 19 | output += "\n"; 20 | } 21 | 22 | fs.writeFileSync('./blank.csv', output); -------------------------------------------------------------------------------- /assets/bullet.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tlhunter/robot-onslaught/e5e73c90bbdc6131912efaa661c5475e412fa39b/assets/bullet.xcf -------------------------------------------------------------------------------- /assets/heart.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tlhunter/robot-onslaught/e5e73c90bbdc6131912efaa661c5475e412fa39b/assets/heart.xcf -------------------------------------------------------------------------------- /assets/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tlhunter/robot-onslaught/e5e73c90bbdc6131912efaa661c5475e412fa39b/assets/screenshot.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | "use strict"; 4 | 5 | var express = require('express'); 6 | var app = express(); 7 | var uuid = require('uuid'); 8 | var PUBNUB = require("pubnub"); 9 | 10 | var port = parseInt(process.argv[2], 10) || 9000; 11 | var pubnub_config = require('./public/config/pubnub.json'); 12 | var server_id = uuid.v4(); 13 | 14 | var pubnub = PUBNUB({ 15 | publish_key: pubnub_config.publish, 16 | subscribe_key: pubnub_config.subscribe 17 | }); 18 | 19 | pubnub.subscribe({ 20 | channel: pubnub_config.channel, 21 | ssl: true, 22 | uuid: server_id, 23 | message: function (data) { 24 | console.log(data); 25 | }, 26 | connect: function() { 27 | console.log('Subscribed to PubNub:' + pubnub_config.channel); 28 | }, 29 | presence: function (data) { 30 | console.log('presence', data); 31 | } 32 | }); 33 | 34 | app.get('/', function (req, res) { 35 | res.sendFile(__dirname + '/public/index.html'); 36 | }); 37 | 38 | app.use(express.static(__dirname + '/public')); 39 | 40 | app.listen(port); 41 | 42 | console.log("Listening on ANY_ADDR:" + port) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "robot-onslaught", 3 | "version": "0.0.1", 4 | "description": "Entry into the PubNub 2014 Game Competition", 5 | "main": "simple-server.js", 6 | "private": true, 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git@github.com:tlhunter/robot-onslaught.git" 13 | }, 14 | "author": "Thomas Hunter II ", 15 | "license": "BSD", 16 | "bugs": { 17 | "url": "https://github.com/tlhunter/robot-onslaught/issues" 18 | }, 19 | "homepage": "https://github.com/tlhunter/robot-onslaught", 20 | "dependencies": { 21 | "express": "^4.9.5", 22 | "pubnub": "^3.6.7", 23 | "uuid": "^2.0.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /public/audio/background.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tlhunter/robot-onslaught/e5e73c90bbdc6131912efaa661c5475e412fa39b/public/audio/background.mp3 -------------------------------------------------------------------------------- /public/audio/damage.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tlhunter/robot-onslaught/e5e73c90bbdc6131912efaa661c5475e412fa39b/public/audio/damage.wav -------------------------------------------------------------------------------- /public/audio/death.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tlhunter/robot-onslaught/e5e73c90bbdc6131912efaa661c5475e412fa39b/public/audio/death.wav -------------------------------------------------------------------------------- /public/audio/pickup.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tlhunter/robot-onslaught/e5e73c90bbdc6131912efaa661c5475e412fa39b/public/audio/pickup.wav -------------------------------------------------------------------------------- /public/audio/shoot.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tlhunter/robot-onslaught/e5e73c90bbdc6131912efaa661c5475e412fa39b/public/audio/shoot.wav -------------------------------------------------------------------------------- /public/audio/spawn.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tlhunter/robot-onslaught/e5e73c90bbdc6131912efaa661c5475e412fa39b/public/audio/spawn.wav -------------------------------------------------------------------------------- /public/config/pubnub.json: -------------------------------------------------------------------------------- 1 | { 2 | "subscribe": "demo", 3 | "publish": "demo", 4 | "channel": "level:0:0" 5 | } -------------------------------------------------------------------------------- /public/data/level_0_0.csv: -------------------------------------------------------------------------------- 1 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 2 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 3 | 0,0,0,0,1,1,1,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 4 | 0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,1,1,1,1,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 5 | 0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 6 | 0,0,0,0,1,0,0,0,1,0,1,1,1,1,1,1,0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 7 | 0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,1,0,0,1,0,20,0,1,0,0,1,0,0,20,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 8 | 0,0,0,0,0,0,0,0,1,0,1,0,0,20,0,1,0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 9 | 0,0,1,1,1,1,1,1,1,0,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 10 | 0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,1,1,1,1,1,1,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 11 | 0,0,1,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,24,1,23,1,23,1,23,1,24,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,20,20,20,20,20,20,20,0,0 12 | 0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,20,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,1,1,1,1,1,1,1,20,0,0 13 | 0,0,1,0,1,0,0,0,0,0,0,1,0,20,0,1,1,1,23,1,10,1,20,1,10,1,23,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,20,1,2,2,2,2,2,1,20,0,0 14 | 0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,2,2,2,2,2,1,20,0,0 15 | 0,0,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,23,1,20,1,10,1,20,1,23,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,1,20,0,0 16 | 0,0,0,0,0,1,0,0,1,20,20,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,2,2,2,2,2,1,20,0,0 17 | 0,0,0,0,0,1,1,1,1,20,20,1,0,20,0,1,1,1,23,1,10,1,20,1,10,1,23,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,20,1,2,2,2,2,2,1,20,0,0 18 | 0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,20,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,1,1,1,1,1,1,1,20,0,0 19 | 0,0,0,0,0,0,0,0,1,0,0,1,1,0,0,1,0,0,24,1,23,1,23,1,23,1,24,0,0,1,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,20,20,20,20,20,20,20,20,20,0,0 20 | 0,0,0,0,0,1,1,1,1,0,0,0,1,0,0,1,0,0,0,1,1,1,1,1,1,1,0,0,0,1,0,0,0,1,21,1,22,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 21 | 0,0,0,0,0,0,0,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,10,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 22 | 0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,1,0,0,20,0,1,22,1,21,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 23 | 0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,20,0,1,0,0,0,0,20,0,1,0,1,0,1,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 24 | 0,0,0,0,0,0,0,1,0,0,1,1,1,0,0,0,0,1,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 25 | 0,0,0,0,0,0,0,1,1,1,1,23,1,0,1,0,0,1,0,0,0,0,0,0,1,1,1,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 26 | 0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,1,1,1,1,0,1,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 27 | 0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,1,1,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 28 | 0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,1,1,1,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 29 | 0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 30 | 0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 31 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 32 | -------------------------------------------------------------------------------- /public/images/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tlhunter/robot-onslaught/e5e73c90bbdc6131912efaa661c5475e412fa39b/public/images/avatar.png -------------------------------------------------------------------------------- /public/images/bullet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tlhunter/robot-onslaught/e5e73c90bbdc6131912efaa661c5475e412fa39b/public/images/bullet.png -------------------------------------------------------------------------------- /public/images/embed-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tlhunter/robot-onslaught/e5e73c90bbdc6131912efaa661c5475e412fa39b/public/images/embed-screenshot.png -------------------------------------------------------------------------------- /public/images/health.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tlhunter/robot-onslaught/e5e73c90bbdc6131912efaa661c5475e412fa39b/public/images/health.png -------------------------------------------------------------------------------- /public/images/heart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tlhunter/robot-onslaught/e5e73c90bbdc6131912efaa661c5475e412fa39b/public/images/heart.png -------------------------------------------------------------------------------- /public/images/terrain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tlhunter/robot-onslaught/e5e73c90bbdc6131912efaa661c5475e412fa39b/public/images/terrain.png -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Robot Onslaught 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |

Robot Onslaught

23 |
24 | Establishing PubNub connection... 25 |
26 |
27 | PubNub 2014 Game Entry by Thomas Hunter. 28 | Fork Me on GitHub.
29 | Use WASD keys to move, Arrow keys to aim, and SPACE to shoot. 30 |
31 | 32 | 33 | 37 | -------------------------------------------------------------------------------- /public/scripts/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": false, 3 | "browser": true, 4 | "jquery": true, 5 | "globalstrict": true, 6 | "devel": true, 7 | "loopfunc": true, 8 | "globals": { 9 | "client_id": true, 10 | "PUBNUB": true 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /public/scripts/bullets.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var Bullets = function(game) { 4 | this.game = game; 5 | 6 | this.bullets = []; 7 | }; 8 | 9 | Bullets.prototype.SPEED = 600; 10 | 11 | Bullets.prototype.preload = function() { 12 | this.game.load.spritesheet('bullet', 'images/bullet.png', 24, 24); 13 | }; 14 | 15 | Bullets.prototype.create = function() { 16 | 17 | }; 18 | 19 | Bullets.prototype.update = function() { 20 | for (var i = 0; i < this.bullets.length; i++) { 21 | this.game.physics.arcade.collide(this.bullets[i].entity, player.entity, this.shotHandler); 22 | this.game.physics.arcade.collide(this.bullets[i].entity, world.middleground, this.collisionHandler); 23 | this.game.physics.arcade.collide(this.bullets[i].entity, world.hostileground, this.enemyHitHandler); 24 | } 25 | }; 26 | 27 | Bullets.prototype.collisionHandler = function(bullet, collide) { 28 | console.log('bullet destroy'); 29 | bullet.kill(); 30 | // TODO: remove from array 31 | }; 32 | 33 | Bullets.prototype.shotHandler = function (bullet, player_body) { 34 | console.log("I've been hit!"); 35 | 36 | bullet.kill(); 37 | 38 | player.hurt(); 39 | }; 40 | 41 | Bullets.prototype.enemyHitHandler = function(bullet, enemy_body) { 42 | console.log('enemy hit'); 43 | world.sounds.damage.play(); 44 | 45 | bullet.kill(); 46 | 47 | return false; 48 | }; 49 | 50 | Bullets.prototype.add = function(bullet) { 51 | bullet.entity = this.game.add.sprite(bullet.x, bullet.y, 'bullet'); 52 | this.game.physics.enable(bullet.entity, Phaser.Physics.ARCADE); 53 | 54 | switch (bullet.dir) { 55 | case 0: 56 | bullet.entity.animations.add('north', [0, 1], 8, true); 57 | bullet.entity.animations.play('north'); 58 | bullet.entity.body.setSize(6, 10, 9, 18); 59 | bullet.entity.body.velocity.y = -this.SPEED; 60 | bullet.entity.body.checkCollision.up = true; 61 | bullet.entity.body.checkCollision.down = false; 62 | bullet.entity.body.checkCollision.right = false; 63 | bullet.entity.body.checkCollision.left = false; 64 | break; 65 | 66 | case 1: 67 | bullet.entity.animations.add('east', [2, 3], 8, true); 68 | bullet.entity.animations.play('east'); 69 | bullet.entity.body.setSize(10, 6, 14, 27); 70 | bullet.entity.body.velocity.x = this.SPEED; 71 | bullet.entity.body.checkCollision.up = false; 72 | bullet.entity.body.checkCollision.down = false; 73 | bullet.entity.body.checkCollision.right = true; 74 | bullet.entity.body.checkCollision.left = false; 75 | break; 76 | 77 | case 2: 78 | bullet.entity.animations.add('south', [4, 5], 8, true); 79 | bullet.entity.animations.play('south'); 80 | bullet.entity.body.setSize(6, 10, 9, 38); 81 | bullet.entity.body.velocity.y = this.SPEED; 82 | bullet.entity.body.checkCollision.up = false; 83 | bullet.entity.body.checkCollision.down = true; 84 | bullet.entity.body.checkCollision.right = false; 85 | bullet.entity.body.checkCollision.left = false; 86 | break; 87 | 88 | case 3: 89 | bullet.entity.animations.add('west', [6, 7], 8, true); 90 | bullet.entity.animations.play('west'); 91 | bullet.entity.body.setSize(10, 6, 0, 27); 92 | bullet.entity.body.velocity.x = -this.SPEED; 93 | bullet.entity.body.checkCollision.up = false; 94 | bullet.entity.body.checkCollision.down = false; 95 | bullet.entity.body.checkCollision.right = false; 96 | bullet.entity.body.checkCollision.left = true; 97 | break; 98 | 99 | default: 100 | console.log('INVALID BULLET', bullet); 101 | return; 102 | } 103 | 104 | world.middleground.add(bullet.entity); 105 | 106 | this.bullets.push(bullet); 107 | }; -------------------------------------------------------------------------------- /public/scripts/enemies.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var Enemies = function(game) { 4 | this.game = game; 5 | 6 | this.enemies = {}; 7 | }; 8 | 9 | Enemies.prototype.EXPIRE = 5000; 10 | 11 | Enemies.prototype.preload = function() { 12 | 13 | }; 14 | 15 | Enemies.prototype.create = function() { 16 | 17 | }; 18 | 19 | Enemies.prototype.update = function() { 20 | var enemy_ids = Object.keys(this.enemies); 21 | var now = Date.now(); 22 | 23 | for (var i = 0; i < enemy_ids.length; i++) { 24 | var enemy_id = enemy_ids[i]; 25 | var enemy = this.enemies[enemy_id]; 26 | if (enemy.last_seen < now - this.EXPIRE) { 27 | enemy.entity.kill(); 28 | delete this.enemies[enemy_id]; 29 | console.log('EXPIRE', enemy_id); 30 | } 31 | } 32 | }; 33 | 34 | Enemies.prototype.add = function(enemy) { 35 | enemy.last_seen = Date.now(); 36 | 37 | if (this.enemies[enemy.client]) { 38 | this.enemies[enemy.client].entity.x = enemy.x; 39 | this.enemies[enemy.client].entity.y = enemy.y; 40 | this.enemies[enemy.client].entity.animations.play('alive'); 41 | return; 42 | } 43 | 44 | this.enemies[enemy.client] = enemy; 45 | this.enemies[enemy.client].entity = this.game.add.sprite(enemy.x - 16, enemy.y - 48, 'avatar'); 46 | 47 | var entity = this.enemies[enemy.client].entity; 48 | 49 | this.game.physics.arcade.enable(entity); 50 | 51 | entity.immovable = true; 52 | entity.body.immovable = true; 53 | 54 | entity.animations.add('alive', [0, 1, 0, 2], 2, true); 55 | entity.animations.add('dead', [3], 1000, false); 56 | 57 | entity.animations.play('alive'); 58 | 59 | world.hostileground.add(entity); 60 | }; 61 | 62 | Enemies.prototype.kill = function(enemy) { 63 | if (!this.enemies[enemy.client]) { 64 | return; 65 | } 66 | 67 | enemy.last_seen = Date.now(); 68 | this.enemies[enemy.client].last_seen = enemy.last_seen; 69 | 70 | this.enemies[enemy.client].entity.animations.play('dead'); 71 | // TODO: Delete after period of time 72 | }; 73 | 74 | Enemies.prototype.move = function(enemy) { 75 | enemy.last_seen = Date.now(); 76 | 77 | if (!this.enemies[enemy.client]) { 78 | this.add(enemy); 79 | } 80 | 81 | var entity = this.enemies[enemy.client].entity; 82 | this.enemies[enemy.client].last_seen = enemy.last_seen; 83 | 84 | entity.x = enemy.x - 16; 85 | entity.y = enemy.y - 48; 86 | //entity.z = -1 * Math.floor(enemy.y - 48); 87 | }; 88 | 89 | Enemies.prototype.heartbeat = function(data) { 90 | this.move(data); 91 | }; -------------------------------------------------------------------------------- /public/scripts/hud.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var HUD = function(game) { 4 | this.game = game; 5 | this.text = {}; 6 | this.hud = null; 7 | this.hearts = []; 8 | }; 9 | 10 | HUD.prototype.FONT = { 11 | fill: '#ffffff', 12 | font: '16px "Press Start 2P"', 13 | stroke: '#000000', 14 | strokeThickness: 4 15 | }; 16 | 17 | HUD.prototype.FONT_SMALL = { 18 | fill: '#ffffff', 19 | font: '8px "Press Start 2P"', 20 | stroke: '#000000', 21 | strokeThickness: 2 22 | }; 23 | 24 | HUD.prototype.preload = function() { 25 | this.game.load.spritesheet('heart', 'images/heart.png', 24, 24); 26 | }; 27 | 28 | HUD.prototype.create = function() { 29 | this.hud = this.game.add.group(); 30 | this.hud.enableBody = false; 31 | this.hud.fixedToCamera = true; 32 | 33 | var heart; 34 | for (var i = 0; i < 5; i++) { 35 | // backgrounds 36 | heart = this.game.add.sprite(8 + 28 * i, 8, 'heart', 1); 37 | this.hud.add(heart); 38 | 39 | // red hearts 40 | heart = this.game.add.sprite(8 + 28 * i, 8, 'heart', 0); 41 | this.hearts.push(heart); 42 | this.hud.add(heart); 43 | } 44 | 45 | this.text.debug = this.game.add.text(8, 40, '', this.FONT_SMALL); 46 | 47 | this.hud.add(this.text.debug); 48 | }; 49 | 50 | HUD.prototype.update = function() { 51 | var text = ""; 52 | 53 | var pos = player.getCoordinate(false); 54 | 55 | text += "POS: " + pos.x + "," + pos.y + "\n"; 56 | text += "FPS: " + this.game.time.fps + "/60"; 57 | 58 | this.text.debug.text = text; 59 | 60 | for (var i = 0; i < 5; i++) { 61 | if (i > player.health - 1) { 62 | this.hearts[i].exists = false; 63 | } else { 64 | this.hearts[i].exists = true; 65 | } 66 | } 67 | }; -------------------------------------------------------------------------------- /public/scripts/libraries/progress.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Progress.js v0.1.0 3 | * https://github.com/usablica/progress.js 4 | * MIT licensed 5 | * 6 | * Copyright (C) 2013 usabli.ca - Afshin Mehrabani (@afshinmeh) 7 | */ 8 | 9 | (function (root, factory) { 10 | if (typeof exports === 'object') { 11 | // CommonJS 12 | factory(exports); 13 | } else if (typeof define === 'function' && define.amd) { 14 | // AMD. Register as an anonymous module. 15 | define(['exports'], factory); 16 | } else { 17 | // Browser globals 18 | factory(root); 19 | } 20 | } (this, function (exports) { 21 | //Default config/variables 22 | var VERSION = '0.1.0'; 23 | 24 | /** 25 | * ProgressJs main class 26 | * 27 | * @class ProgressJs 28 | */ 29 | function ProgressJs(obj) { 30 | 31 | if (typeof obj.length != 'undefined') { 32 | this._targetElement = obj; 33 | } else { 34 | this._targetElement = [obj]; 35 | } 36 | 37 | if (typeof window._progressjsId === 'undefined') 38 | window._progressjsId = 1; 39 | 40 | if (typeof window._progressjsIntervals === 'undefined') 41 | window._progressjsIntervals = {}; 42 | 43 | this._options = { 44 | //progress bar theme 45 | theme: 'blue', 46 | //overlay mode makes an overlay layer in the target element 47 | overlayMode: false, 48 | //to consider CSS3 transitions in events 49 | considerTransition: true 50 | }; 51 | } 52 | 53 | /** 54 | * Start progress for specific element(s) 55 | * 56 | * @api private 57 | * @method _createContainer 58 | */ 59 | function _startProgress() { 60 | 61 | //call onBeforeStart callback 62 | if (typeof this._onBeforeStartCallback != 'undefined') { 63 | this._onBeforeStartCallback.call(this); 64 | } 65 | 66 | //create the container for progress bar 67 | _createContainer.call(this); 68 | 69 | for (var i = 0, elmsLength = this._targetElement.length; i < elmsLength; i++) { 70 | _setProgress.call(this, this._targetElement[i]); 71 | } 72 | } 73 | 74 | /** 75 | * Set progress bar for specific element 76 | * 77 | * @api private 78 | * @method _setProgress 79 | * @param {Object} targetElement 80 | */ 81 | function _setProgress(targetElement) { 82 | 83 | //if the target element already as `data-progressjs`, ignore the init 84 | if (targetElement.hasAttribute("data-progressjs")) 85 | return; 86 | 87 | //get target element position 88 | var targetElementOffset = _getOffset.call(this, targetElement); 89 | 90 | targetElement.setAttribute("data-progressjs", window._progressjsId); 91 | 92 | var progressElementContainer = document.createElement('div'); 93 | progressElementContainer.className = 'progressjs-progress progressjs-theme-' + this._options.theme; 94 | 95 | 96 | //set the position percent elements, it depends on targetElement tag 97 | if (targetElement.tagName.toLowerCase() === 'body') { 98 | progressElementContainer.style.position = 'fixed'; 99 | } else { 100 | progressElementContainer.style.position = 'absolute'; 101 | } 102 | 103 | progressElementContainer.setAttribute("data-progressjs", window._progressjsId); 104 | var progressElement = document.createElement("div"); 105 | progressElement.className = "progressjs-inner"; 106 | 107 | //create an element for current percent of progress bar 108 | var progressPercentElement = document.createElement('div'); 109 | progressPercentElement.className = "progressjs-percent"; 110 | progressPercentElement.innerHTML = "1%"; 111 | 112 | progressElement.appendChild(progressPercentElement); 113 | 114 | if (this._options.overlayMode && targetElement.tagName.toLowerCase() === 'body') { 115 | //if we have `body` for target element and also overlay mode is enable, we should use a different 116 | //position for progress bar container element 117 | progressElementContainer.style.left = 0; 118 | progressElementContainer.style.right = 0; 119 | progressElementContainer.style.top = 0; 120 | progressElementContainer.style.bottom = 0; 121 | } else { 122 | //set progress bar container size and offset 123 | progressElementContainer.style.left = targetElementOffset.left + 'px'; 124 | progressElementContainer.style.top = targetElementOffset.top + 'px'; 125 | progressElementContainer.style.width = targetElementOffset.width + 'px'; 126 | 127 | if (this._options.overlayMode) { 128 | progressElementContainer.style.height = targetElementOffset.height + 'px'; 129 | } 130 | } 131 | 132 | progressElementContainer.appendChild(progressElement); 133 | 134 | //append the element to container 135 | var container = document.querySelector('.progressjs-container'); 136 | container.appendChild(progressElementContainer); 137 | 138 | _setPercentFor(targetElement, 1); 139 | 140 | //and increase the progressId 141 | ++window._progressjsId; 142 | } 143 | 144 | /** 145 | * Set percent for all elements 146 | * 147 | * @api private 148 | * @method _setPercent 149 | * @param {Number} percent 150 | */ 151 | function _setPercent(percent) { 152 | for (var i = 0, elmsLength = this._targetElement.length; i < elmsLength; i++) { 153 | _setPercentFor.call(this, this._targetElement[i], percent); 154 | } 155 | } 156 | 157 | /** 158 | * Set percent for specific element 159 | * 160 | * @api private 161 | * @method _setPercentFor 162 | * @param {Object} targetElement 163 | * @param {Number} percent 164 | */ 165 | function _setPercentFor(targetElement, percent) { 166 | var self = this; 167 | 168 | //prevent overflow! 169 | if (percent >= 100) 170 | percent = 100; 171 | 172 | if (targetElement.hasAttribute("data-progressjs")) { 173 | //setTimeout for better CSS3 animation applying in some cases 174 | setTimeout(function() { 175 | 176 | //call the onprogress callback 177 | if (typeof self._onProgressCallback != 'undefined') { 178 | self._onProgressCallback.call(self, targetElement, percent); 179 | } 180 | 181 | var percentElement = _getPercentElement(targetElement); 182 | if (!percentElement) {return;} 183 | percentElement.style.width = parseInt(percent) + '%'; 184 | 185 | var percentElement = percentElement.querySelector(".progressjs-percent"); 186 | var existingPercent = parseInt(percentElement.innerHTML.replace('%', '')); 187 | 188 | //start increase/decrease the percent element with animation 189 | (function(percentElement, existingPercent, currentPercent) { 190 | 191 | var increasement = true; 192 | if (existingPercent > currentPercent) { 193 | increasement = false; 194 | } 195 | 196 | var intervalIn = 10; 197 | function changePercentTimer(percentElement, existingPercent, currentPercent) { 198 | //calculate the distance between two percents 199 | var distance = Math.abs(existingPercent - currentPercent); 200 | if (distance < 3) { 201 | intervalIn = 30; 202 | } else if (distance < 20) { 203 | intervalIn = 20; 204 | } else { 205 | intervanIn = 1; 206 | } 207 | 208 | if ((existingPercent - currentPercent) != 0) { 209 | //set the percent 210 | percentElement.innerHTML = (increasement ? (++existingPercent) : (--existingPercent)) + '%'; 211 | setTimeout(function() { changePercentTimer(percentElement, existingPercent, currentPercent); }, intervalIn); 212 | } 213 | } 214 | 215 | changePercentTimer(percentElement, existingPercent, currentPercent); 216 | 217 | })(percentElement, existingPercent, parseInt(percent)); 218 | 219 | }, 50); 220 | } 221 | } 222 | 223 | /** 224 | * Get the progress bar element 225 | * 226 | * @api private 227 | * @method _getPercentElement 228 | * @param {Object} targetElement 229 | */ 230 | function _getPercentElement(targetElement) { 231 | var progressjsId = parseInt(targetElement.getAttribute('data-progressjs')); 232 | return document.querySelector('.progressjs-container > .progressjs-progress[data-progressjs="' + progressjsId + '"] > .progressjs-inner'); 233 | } 234 | 235 | /** 236 | * Auto increase the progress bar every X milliseconds 237 | * 238 | * @api private 239 | * @method _autoIncrease 240 | * @param {Number} size 241 | * @param {Number} millisecond 242 | */ 243 | function _autoIncrease(size, millisecond) { 244 | var self = this; 245 | 246 | var progressjsId = parseInt(this._targetElement[0].getAttribute('data-progressjs')); 247 | 248 | if (typeof window._progressjsIntervals[progressjsId] != 'undefined') { 249 | clearInterval(window._progressjsIntervals[progressjsId]); 250 | } 251 | window._progressjsIntervals[progressjsId] = setInterval(function() { 252 | _increasePercent.call(self, size); 253 | }, millisecond); 254 | } 255 | 256 | /** 257 | * Increase the size of progress bar 258 | * 259 | * @api private 260 | * @method _increasePercent 261 | * @param {Number} size 262 | */ 263 | function _increasePercent(size) { 264 | for (var i = 0, elmsLength = this._targetElement.length; i < elmsLength; i++) { 265 | var currentElement = this._targetElement[i]; 266 | if (currentElement.hasAttribute('data-progressjs')) { 267 | var percentElement = _getPercentElement(currentElement); 268 | var existingPercent = parseInt(percentElement.style.width.replace('%', '')); 269 | if (existingPercent) { 270 | _setPercentFor.call(this, currentElement, existingPercent + (size || 1)); 271 | } 272 | } 273 | } 274 | } 275 | 276 | /** 277 | * Close and remove progress bar 278 | * 279 | * @api private 280 | * @method _end 281 | */ 282 | function _end() { 283 | 284 | //call onBeforeEnd callback 285 | if (typeof this._onBeforeEndCallback != 'undefined') { 286 | if (this._options.considerTransition === true) { 287 | //we can safety assume that all layers would be the same, so `this._targetElement[0]` is the same as `this._targetElement[1]` 288 | _getPercentElement(this._targetElement[0]).addEventListener(whichTransitionEvent(), this._onBeforeEndCallback, false); 289 | } else { 290 | this._onBeforeEndCallback.call(this); 291 | } 292 | } 293 | 294 | var progressjsId = parseInt(this._targetElement[0].getAttribute('data-progressjs')); 295 | 296 | for (var i = 0, elmsLength = this._targetElement.length; i < elmsLength; i++) { 297 | var currentElement = this._targetElement[i]; 298 | var percentElement = _getPercentElement(currentElement); 299 | 300 | if (!percentElement) 301 | return; 302 | 303 | var existingPercent = parseInt(percentElement.style.width.replace('%', '')); 304 | 305 | var timeoutSec = 1; 306 | if (existingPercent < 100) { 307 | _setPercentFor.call(this, currentElement, 100); 308 | timeoutSec = 500; 309 | } 310 | 311 | //I believe I should handle this situation with eventListener and `transitionend` event but I'm not sure 312 | //about compatibility with IEs. Should be fixed in further versions. 313 | (function(percentElement, currentElement) { 314 | setTimeout(function() { 315 | percentElement.parentNode.className += " progressjs-end"; 316 | 317 | setTimeout(function() { 318 | //remove the percent element from page 319 | percentElement.parentNode.parentNode.removeChild(percentElement.parentNode); 320 | //and remove the attribute 321 | currentElement.removeAttribute("data-progressjs"); 322 | }, 1000); 323 | }, timeoutSec); 324 | })(percentElement, currentElement); 325 | } 326 | 327 | //clean the setInterval for autoIncrease function 328 | if (window._progressjsIntervals[progressjsId]) { 329 | //`delete` keyword has some problems in IE 330 | try { 331 | clearInterval(window._progressjsIntervals[progressjsId]); 332 | window._progressjsIntervals[progressjsId] = null; 333 | delete window._progressjsIntervals[progressjsId]; 334 | } catch(ex) { } 335 | } 336 | } 337 | 338 | /** 339 | * Create the progress bar container 340 | * 341 | * @api private 342 | * @method _createContainer 343 | */ 344 | function _createContainer() { 345 | //first check if we have an container already, we don't need to create it again 346 | if (!document.querySelector(".progressjs-container")) { 347 | var containerElement = document.createElement("div"); 348 | containerElement.className = "progressjs-container"; 349 | document.body.appendChild(containerElement); 350 | } 351 | } 352 | 353 | /** 354 | * Get an element position on the page 355 | * Thanks to `meouw`: http://stackoverflow.com/a/442474/375966 356 | * 357 | * @api private 358 | * @method _getOffset 359 | * @param {Object} element 360 | * @returns Element's position info 361 | */ 362 | function _getOffset(element) { 363 | var elementPosition = {}; 364 | 365 | if (element.tagName.toLowerCase() === 'body') { 366 | //set width 367 | elementPosition.width = element.clientWidth; 368 | //set height 369 | elementPosition.height = element.clientHeight; 370 | } else { 371 | //set width 372 | elementPosition.width = element.offsetWidth; 373 | //set height 374 | elementPosition.height = element.offsetHeight; 375 | } 376 | 377 | //calculate element top and left 378 | var _x = 0; 379 | var _y = 0; 380 | while (element && !isNaN(element.offsetLeft) && !isNaN(element.offsetTop)) { 381 | _x += element.offsetLeft; 382 | _y += element.offsetTop; 383 | element = element.offsetParent; 384 | } 385 | //set top 386 | elementPosition.top = _y; 387 | //set left 388 | elementPosition.left = _x; 389 | 390 | return elementPosition; 391 | } 392 | 393 | /** 394 | * Overwrites obj1's values with obj2's and adds obj2's if non existent in obj1 395 | * via: http://stackoverflow.com/questions/171251/how-can-i-merge-properties-of-two-javascript-objects-dynamically 396 | * 397 | * @param obj1 398 | * @param obj2 399 | * @returns obj3 a new object based on obj1 and obj2 400 | */ 401 | function _mergeOptions(obj1, obj2) { 402 | var obj3 = {}; 403 | for (var attrname in obj1) { obj3[attrname] = obj1[attrname]; } 404 | for (var attrname in obj2) { obj3[attrname] = obj2[attrname]; } 405 | return obj3; 406 | } 407 | 408 | var progressJs = function (targetElm) { 409 | if (typeof (targetElm) === 'object') { 410 | //Ok, create a new instance 411 | return new ProgressJs(targetElm); 412 | 413 | } else if (typeof (targetElm) === 'string') { 414 | //select the target element with query selector 415 | var targetElement = document.querySelectorAll(targetElm); 416 | 417 | if (targetElement) { 418 | return new ProgressJs(targetElement); 419 | } else { 420 | throw new Error('There is no element with given selector.'); 421 | } 422 | } else { 423 | return new ProgressJs(document.body); 424 | } 425 | }; 426 | 427 | /** 428 | * Get correct transition callback 429 | * Thanks @webinista: http://stackoverflow.com/a/9090128/375966 430 | * 431 | * @returns transition name 432 | */ 433 | function whichTransitionEvent() { 434 | var t; 435 | var el = document.createElement('fakeelement'); 436 | var transitions = { 437 | 'transition': 'transitionend', 438 | 'OTransition': 'oTransitionEnd', 439 | 'MozTransition': 'transitionend', 440 | 'WebkitTransition': 'webkitTransitionEnd' 441 | } 442 | 443 | for (t in transitions) { 444 | if (el.style[t] !== undefined) { 445 | return transitions[t]; 446 | } 447 | } 448 | } 449 | 450 | /** 451 | * Current ProgressJs version 452 | * 453 | * @property version 454 | * @type String 455 | */ 456 | progressJs.version = VERSION; 457 | 458 | //Prototype 459 | progressJs.fn = ProgressJs.prototype = { 460 | clone: function () { 461 | return new ProgressJs(this); 462 | }, 463 | setOption: function(option, value) { 464 | this._options[option] = value; 465 | return this; 466 | }, 467 | setOptions: function(options) { 468 | this._options = _mergeOptions(this._options, options); 469 | return this; 470 | }, 471 | start: function() { 472 | _startProgress.call(this); 473 | return this; 474 | }, 475 | set: function(percent) { 476 | _setPercent.call(this, percent); 477 | return this; 478 | }, 479 | increase: function(size) { 480 | _increasePercent.call(this, size); 481 | return this; 482 | }, 483 | autoIncrease: function(size, millisecond) { 484 | _autoIncrease.call(this, size, millisecond); 485 | return this; 486 | }, 487 | end: function() { 488 | _end.call(this); 489 | return this; 490 | }, 491 | onbeforeend: function(providedCallback) { 492 | if (typeof (providedCallback) === 'function') { 493 | this._onBeforeEndCallback = providedCallback; 494 | } else { 495 | throw new Error('Provided callback for onbeforeend was not a function'); 496 | } 497 | return this; 498 | }, 499 | onbeforestart: function(providedCallback) { 500 | if (typeof (providedCallback) === 'function') { 501 | this._onBeforeStartCallback = providedCallback; 502 | } else { 503 | throw new Error('Provided callback for onbeforestart was not a function'); 504 | } 505 | return this; 506 | }, 507 | onprogress: function(providedCallback) { 508 | if (typeof (providedCallback) === 'function') { 509 | this._onProgressCallback = providedCallback; 510 | } else { 511 | throw new Error('Provided callback for onprogress was not a function'); 512 | } 513 | return this; 514 | } 515 | }; 516 | 517 | exports.progressJs = progressJs; 518 | return progressJs; 519 | })); 520 | -------------------------------------------------------------------------------- /public/scripts/main.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var world, player, hud, pickups, enemies, bullets, game; 4 | 5 | var client_id; 6 | var pubnub; 7 | var channel; 8 | 9 | $(function() { 10 | progressJs().setOptions({ 11 | overlayMode: true, 12 | theme: 'onslaught' 13 | }).start(); 14 | 15 | $.get('config/pubnub.json', function (data) { 16 | client_id = PUBNUB.uuid(); 17 | channel = data.channel; 18 | 19 | pubnub = PUBNUB.init({ 20 | publish_key: data.publish, 21 | subscribe_key: data.subscribe, 22 | ssl: true 23 | }); 24 | 25 | pubnub.subscribe({ 26 | channel: channel, 27 | 28 | message: function(data) { 29 | if (data.client === client_id || game.load.progress < 100) { 30 | return; 31 | } 32 | 33 | console.log(data); 34 | 35 | if (data.type === 'spawn') { 36 | world.sounds.spawn.play(); 37 | enemies.add(data); 38 | } else if (data.type === 'shoot') { 39 | world.sounds.shoot.play(); 40 | bullets.add(data); 41 | // TODO: bullet magic 42 | } else if (data.type === 'death') { 43 | world.sounds.death.play(); 44 | enemies.kill(data); 45 | } else if (data.type === 'pickup') { 46 | world.sounds.pickup.play(); 47 | // TODO: Remove from pickups 48 | } else if (data.type === 'move') { 49 | enemies.move(data); 50 | } else if (data.type === 'heartbeat') { 51 | enemies.heartbeat(data); 52 | } 53 | }, 54 | connect: function() { 55 | $('#gamefield span').hide(); 56 | 57 | init(); 58 | } 59 | }); 60 | }); 61 | }); 62 | 63 | function init() { 64 | game = new Phaser.Game(24*32, 14*32, Phaser.AUTO, 'gamefield', { 65 | preload: function() { 66 | world.preload(); 67 | hud.preload(); 68 | player.preload(); 69 | pickups.preload(); 70 | enemies.preload(); 71 | bullets.preload(); 72 | }, 73 | 74 | create: function() { 75 | progressJs().end(); 76 | world.create(); 77 | hud.create(); 78 | player.create(); 79 | pickups.create(); 80 | enemies.create(); 81 | bullets.create(); 82 | }, 83 | 84 | update: function() { 85 | world.update(); 86 | hud.update(); 87 | player.update(); 88 | pickups.update(); 89 | enemies.update(); 90 | bullets.update(); 91 | }, 92 | 93 | loadUpdate: function() { 94 | progressJs().set(game.load.progress); 95 | } 96 | }); 97 | 98 | world = new World(game); 99 | player = new Player(game); 100 | hud = new HUD(game); 101 | pickups = new Pickups(game); 102 | enemies = new Enemies(game); 103 | bullets = new Bullets(game); 104 | } 105 | 106 | function reportLocation(x, y) { 107 | pubnub.publish({ 108 | channel: channel, 109 | message: { 110 | type: 'move', 111 | client: client_id, 112 | x: Math.round(x), // bandwidth > accuracy 113 | y: Math.round(y) 114 | } 115 | }); 116 | } 117 | 118 | function reportSpawn(x, y) { 119 | pubnub.publish({ 120 | channel: channel, 121 | message: { 122 | type: 'spawn', 123 | client: client_id, 124 | x: Math.round(x), 125 | y: Math.round(y) 126 | } 127 | }); 128 | } 129 | 130 | function reportShoot(bullet) { 131 | pubnub.publish({ 132 | channel: channel, 133 | message: { 134 | type: 'shoot', 135 | client: client_id, 136 | x: Math.round(bullet.x), 137 | y: Math.round(bullet.y), 138 | dir: bullet.dir 139 | } 140 | }); 141 | } 142 | 143 | function reportDeath() { 144 | pubnub.publish({ 145 | channel: channel, 146 | message: { 147 | type: 'death', 148 | client: client_id 149 | } 150 | }); 151 | } 152 | 153 | function reportHeartbeat(x, y) { 154 | pubnub.publish({ 155 | channel: channel, 156 | message: { 157 | type: 'heartbeat', 158 | client: client_id, 159 | x: Math.round(x), 160 | y: Math.round(y) 161 | } 162 | }); 163 | } -------------------------------------------------------------------------------- /public/scripts/pickups.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var Pickups = function(game) { 4 | this.game = game; 5 | }; 6 | 7 | Pickups.prototype.preload = function() { 8 | this.game.load.image('health', 'images/health.png'); 9 | 10 | }; 11 | 12 | Pickups.prototype.create = function() { 13 | 14 | }; 15 | 16 | Pickups.prototype.update = function() { 17 | 18 | }; -------------------------------------------------------------------------------- /public/scripts/player.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var Player = function(game) { 4 | this.game = game; 5 | 6 | this.entity = null; 7 | this.health = 5; 8 | 9 | this.keyboard = null; 10 | 11 | // Delay between shots fired, MS 12 | this.SHOOT_DELAY = 350; 13 | // Time the last bullet was shot 14 | this.last_shoot = 0; 15 | 16 | // Time the last position was sent to pubnub 17 | this.last_reported_position = 0; 18 | // Delay between reporting messages to pubnub, MS 19 | this.POSITION_DELAY = 50; 20 | // Have we moved and need to tell pubnub? 21 | this.position_dirty = true; 22 | 23 | this.HEARTBEAT_DELAY = 2000; 24 | }; 25 | 26 | Player.prototype.preload = function() { 27 | this.game.load.spritesheet('avatar', 'images/avatar.png', 64, 64); 28 | 29 | this.keyboard = this.game.input.keyboard; 30 | }; 31 | 32 | Player.prototype.create = function() { 33 | this.entity = this.game.add.sprite(world.BLOCK.w * 50, world.BLOCK.h * 50 - 24, 'avatar'); 34 | this.game.physics.arcade.enable(this.entity); 35 | this.entity.body.collideWorldBounds = true; 36 | 37 | this.entity.body.setSize(30, 20, 16, 48); 38 | 39 | this.entity.animations.add('alive', [0, 1, 0, 2], 2, true); 40 | this.entity.animations.add('dead', [3], 1000, false); 41 | 42 | world.middleground.add(this.entity); 43 | 44 | this.spawn(); 45 | 46 | var self = this; 47 | 48 | setInterval(function() { 49 | reportHeartbeat(self.entity.body.x, self.entity.body.y) 50 | }, this.HEARTBEAT_DELAY) 51 | }; 52 | 53 | Player.prototype.spawn = function() { 54 | var spawn = world.spawn_locations[Math.floor(Math.random() * world.spawn_locations.length)]; 55 | this.entity.x = world.BLOCK.w * spawn.x; 56 | this.entity.y = world.BLOCK.h * spawn.y - 24; 57 | this.health = 5; 58 | this.entity.animations.play('alive'); 59 | world.sounds.spawn.play(); 60 | reportSpawn(this.entity.x, this.entity.y); 61 | }; 62 | 63 | Player.prototype.update = function() { 64 | if (this.health <= 0) { 65 | return; 66 | } 67 | 68 | var time = Date.now(); 69 | 70 | this.entity.body.velocity.x = this.entity.body.velocity.y = 0; 71 | var moving = false; 72 | 73 | if (this.goNorth()) { 74 | this.entity.body.velocity.y -= 200; 75 | moving = true; 76 | } 77 | 78 | if (this.goSouth()) { 79 | this.entity.body.velocity.y += 200; 80 | moving = true; 81 | } 82 | 83 | if (this.goWest()) { 84 | this.entity.body.velocity.x -= 200; 85 | moving = true; 86 | } 87 | 88 | if (this.goEast()) { 89 | this.entity.body.velocity.x += 200; 90 | moving = true; 91 | } 92 | 93 | if (moving) { 94 | this.position_dirty = true; 95 | } 96 | 97 | if (this.aimNorth() && this.last_shoot < time - this.SHOOT_DELAY) { 98 | this.shoot(0, time); 99 | } 100 | 101 | if (this.aimEast() && this.last_shoot < time - this.SHOOT_DELAY) { 102 | this.shoot(1, time); 103 | } 104 | 105 | if (this.aimSouth() && this.last_shoot < time - this.SHOOT_DELAY) { 106 | this.shoot(2, time); 107 | } 108 | 109 | if (this.aimWest() && this.last_shoot < time - this.SHOOT_DELAY) { 110 | this.shoot(3, time); 111 | } 112 | 113 | if (this.position_dirty && (this.last_reported_position < time - this.POSITION_DELAY)) { 114 | this.position_dirty = false; 115 | this.last_reported_position = time; 116 | reportLocation(this.entity.body.x, this.entity.body.y); 117 | } 118 | 119 | // Phaser seems incapable of snapping to full pixels :'( 120 | if (!moving) { 121 | this.entity.body.x = Math.round(this.entity.body.x); 122 | this.entity.body.y = Math.round(this.entity.body.y); 123 | } 124 | 125 | this.game.camera.follow(this.entity); 126 | }; 127 | 128 | Player.prototype.hurt = function() { 129 | this.health -= 1; 130 | 131 | if (this.health <= 0) { 132 | this.kill(); 133 | 134 | return; 135 | } 136 | 137 | world.sounds.damage.play(); 138 | }; 139 | 140 | Player.prototype.kill = function() { 141 | world.sounds.death.play(); 142 | this.entity.animations.play('dead'); 143 | var self = this; 144 | 145 | reportDeath(); 146 | 147 | setTimeout( 148 | function() { 149 | self.spawn(); 150 | }, 2000 151 | ) 152 | }; 153 | 154 | Player.prototype.shoot = function(dir, time) { 155 | world.sounds.shoot.play(); 156 | var xmod = 0; 157 | var ymod = 0; 158 | 159 | switch(dir) { 160 | case 0: 161 | xmod = 2; 162 | ymod = -48; 163 | break; 164 | case 1: 165 | xmod = 16; 166 | ymod = -32; 167 | break; 168 | case 2: 169 | xmod = 2; 170 | ymod = -16; 171 | break; 172 | case 3: 173 | xmod = -8; 174 | ymod = -32; 175 | break; 176 | } 177 | 178 | var bullet = { 179 | x: this.entity.body.x + xmod, 180 | y: this.entity.body.y + ymod, 181 | dir: dir 182 | }; 183 | 184 | bullets.add(bullet); 185 | 186 | this.last_shoot = time; 187 | 188 | reportShoot(bullet); 189 | }; 190 | 191 | Player.prototype.getCoordinate = function(accurate) { 192 | if (accurate) { 193 | return { 194 | x: this.entity.body.x / world.BLOCK.w, 195 | y: this.entity.body.y / world.BLOCK.h 196 | } 197 | } else { 198 | return { 199 | x: Math.floor(this.entity.body.x / world.BLOCK.w), 200 | y: Math.floor(this.entity.body.y / world.BLOCK.h) 201 | } 202 | } 203 | }; 204 | 205 | Player.prototype.goNorth = function() { 206 | return this.keyboard.isDown(Phaser.Keyboard.W); 207 | }; 208 | 209 | Player.prototype.goSouth = function() { 210 | return this.keyboard.isDown(Phaser.Keyboard.S); 211 | }; 212 | 213 | Player.prototype.goWest = function() { 214 | return this.keyboard.isDown(Phaser.Keyboard.A); 215 | }; 216 | 217 | Player.prototype.goEast = function() { 218 | return this.keyboard.isDown(Phaser.Keyboard.D); 219 | }; 220 | 221 | Player.prototype.aimNorth = function() { 222 | return this.keyboard.isDown(Phaser.Keyboard.UP); 223 | }; 224 | 225 | Player.prototype.aimSouth = function() { 226 | return this.keyboard.isDown(Phaser.Keyboard.DOWN); 227 | }; 228 | 229 | Player.prototype.aimWest = function() { 230 | return this.keyboard.isDown(Phaser.Keyboard.LEFT); 231 | }; 232 | 233 | Player.prototype.aimEast = function() { 234 | return this.keyboard.isDown(Phaser.Keyboard.RIGHT); 235 | }; -------------------------------------------------------------------------------- /public/scripts/world.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var World = function(game) { 4 | this.game = game; 5 | 6 | this.music = null; 7 | this.sounds = {}; 8 | 9 | this.foreground = null; 10 | this.middleground = null; 11 | this.background = null; 12 | 13 | this.spawn_locations = []; 14 | this.SPAWN_TILE = 10; 15 | }; 16 | 17 | World.prototype.BLOCK = { 18 | w: 64, 19 | h: 64 20 | }; 21 | 22 | World.prototype.preload = function() { 23 | this.game.stage.backgroundColor = '#1f1d2c'; 24 | 25 | // Terrain Spritesheet 26 | this.game.load.spritesheet('terrain', 'images/terrain.png', this.BLOCK.w, this.BLOCK.h); 27 | 28 | // Background Music 29 | this.game.load.audio('music', 'audio/background.mp3'); 30 | 31 | // Sound Effects 32 | this.game.load.audio('damage', 'audio/damage.wav'); 33 | this.game.load.audio('death', 'audio/death.wav'); 34 | this.game.load.audio('pickup', 'audio/pickup.wav'); 35 | this.game.load.audio('shoot', 'audio/shoot.wav'); 36 | this.game.load.audio('spawn', 'audio/spawn.wav'); 37 | 38 | // Map Data 39 | this.game.load.tilemap('level', 'data/level_0_0.csv', null, Phaser.Tilemap.CSV); 40 | 41 | // Game Settings 42 | this.game.renderer.roundPixels = true; 43 | this.game.time.advancedTiming = true; 44 | }; 45 | 46 | World.prototype.create = function() { 47 | this.music = game.add.audio('music', 1, true); 48 | 49 | this.sounds = { 50 | damage: this.game.add.audio('damage'), 51 | death: this.game.add.audio('death'), 52 | pickup: this.game.add.audio('pickup'), 53 | shoot: this.game.add.audio('shoot'), 54 | spawn: this.game.add.audio('spawn') 55 | }; 56 | 57 | this.music.play('', 0, 0.1, true); 58 | 59 | this.game.physics.startSystem(Phaser.Physics.ARCADE); 60 | 61 | this.background = this.game.add.group(); 62 | this.background.enableBody = false; 63 | 64 | this.middleground = this.game.add.group(); 65 | this.middleground.enableBody = true; 66 | 67 | this.hostileground = this.game.add.group(); 68 | this.hostileground.enableBody = true; 69 | 70 | this.foreground = this.game.add.group(); 71 | this.foreground.enableBody = false; 72 | 73 | var realBounds = this.buildMap(); 74 | 75 | this.middleground.setAll('body.immovable', true); 76 | 77 | this.game.world.setBounds(0, 0, realBounds[0] * this.BLOCK.w, realBounds[1] * this.BLOCK.h); 78 | }; 79 | 80 | World.prototype.update = function() { 81 | this.game.physics.arcade.collide(player.entity, this.middleground); 82 | //this.game.physics.arcade.collide(pickups.entities, this.foreground); 83 | }; 84 | 85 | World.prototype.parseRawTilesheets = function() { 86 | var level = this.game.cache.getTilemapData('level').data.split('\n'); 87 | 88 | for (var y = 0; y < level.length; y++) { 89 | level[y] = level[y].split(','); 90 | for (var x = 0; x < level[y].length; x++) { 91 | level[y][x] = parseInt(level[y][x], 10); 92 | if (level[y][x] === this.SPAWN_TILE) { 93 | this.spawn_locations.push({ 94 | x: x, 95 | y: y 96 | }); 97 | } 98 | } 99 | } 100 | 101 | return level; 102 | }; 103 | 104 | /** 105 | * This is a heavy process, so optimize the heck out of it 106 | */ 107 | World.prototype.buildMap = function() { 108 | var level = this.parseRawTilesheets(), 109 | tile, 110 | w = this.BLOCK.w, 111 | h = this.BLOCK.h, 112 | tile_offset, 113 | hull, 114 | HEIGHT = level.length, 115 | WIDTH = level[0].length; // Ode to UT's Invisible Collision Hull 116 | 117 | // Never going to look at tiles adjacent to the edge. 118 | // This makes it a lot easier to look for neighbors 119 | // Also, NEVER put a floor on an edge tile 120 | for (var y = 1; y < HEIGHT - 1; y++) { 121 | for (var x = 1; x < WIDTH - 1; x++) { 122 | tile = level[y][x]; 123 | 124 | if (tile === 0) { 125 | // Abyss 126 | if (level[y-1][x]) { 127 | // If there is a floor north of us, and we're abyss, add a shadow 128 | this.background.create(x * w, y * h, 'terrain', 16); 129 | } 130 | 131 | if (level[y-1][x] || level[y][x+1] || level[y][x-1] || level[y+1][x]) { 132 | // If this abyss has a item N, E, S, W of it, then add an invisible foreground for collision 133 | // We only need to look for collision in the direction it faces a walkable tile 134 | hull = this.middleground.create(x * w, y * h, 'terrain', 83); 135 | hull.body.checkCollision.up = !!level[y-1][x]; 136 | hull.body.checkCollision.right = !!level[y][x+1]; 137 | hull.body.checkCollision.down = !!level[y+1][x]; 138 | hull.body.checkCollision.left = !!level[y][x-1]; 139 | } 140 | } else if (tile < 20) { 141 | if (tile === 1) { 142 | // TODO: Lots of ugly logic :( 143 | //this.background.create(x * w, y * h, 'terrain', 20); 144 | tile_offset = this.tileLogic( 145 | level[y-1][x], 146 | level[y-1][x+1], 147 | level[y][x+1], 148 | level[y+1][x+1], 149 | level[y+1][x], 150 | level[y+1][x-1], 151 | level[y][x-1], 152 | level[y-1][x-1] 153 | ); 154 | this.background.create(x * w, y * h, 'terrain', tile_offset); 155 | 156 | } else if (tile === 2) { 157 | this.background.create(x * w, y * h, 'terrain', 4); 158 | } else if (tile === 10) { 159 | this.background.create(x * w, y * h, 'terrain', 62); 160 | } 161 | // Floor 162 | } else if (tile < 40) { 163 | // Solid 164 | if (tile >= 20 && tile <= 22) { 165 | // Block, Computer A, and Computer B have same tops 166 | this.foreground.create(x * w, (y-1) * h, 'terrain', 30); 167 | 168 | if (tile === 20) { 169 | // BLOCK 170 | this.middleground.create(x * w, y * h, 'terrain', 42); 171 | } else if (tile === 21) { 172 | // COMPUTER A 173 | this.middleground.create(x * w, y * h, 'terrain', 43); 174 | } else if (tile === 22) { 175 | // COMPUTER B 176 | this.middleground.create(x * w, y * h, 'terrain', 44); 177 | } 178 | } else if (tile === 23) { 179 | // SOLID A 180 | this.foreground.create(x * w, (y-1) * h, 'terrain', 31); 181 | this.middleground.create(x * w, y * h, 'terrain', 45); 182 | } else if (tile === 24) { 183 | // SOLID B 184 | this.foreground.create(x * w, (y-1) * h, 'terrain', 59); 185 | this.middleground.create(x * w, y * h, 'terrain', 73); 186 | } 187 | } 188 | 189 | } 190 | } 191 | 192 | return [WIDTH, HEIGHT]; 193 | }; 194 | 195 | World.prototype.tileLogic = function (n, ne, e, se, s, sw, w, nw) { 196 | if (n && ne && e && se && s && sw && w && nw) return 20; 197 | if (!n && !ne && !e && !se && !s && !sw && !w && !nw) return 4; 198 | 199 | if (n && ne && e && se && s && sw && w && !nw) return 23; 200 | if (n && ne && e && se && s && !sw && w && nw) return 9; 201 | if (n && ne && e && !se && s && sw && w && nw) return 8; 202 | if (n && !ne && e && se && s && sw && w && nw) return 22; 203 | 204 | if (n && ne && e && se && s && !sw && w && !nw) return 37; 205 | if (n && ne && e && !se && s && !sw && w && nw) return 36; 206 | if (n && !ne && e && !se && s && sw && w && nw) return 39; 207 | if (n && !ne && e && se && s && sw && w && !nw) return 38; 208 | 209 | if (n && !e && s && !sw && w && !nw) return 13; 210 | if (!n && e && !se && s && !sw && w) return 12; 211 | if (n && !ne && e && !se && s && !w) return 26; 212 | if (n && !ne && e && !s && w && !nw) return 27; 213 | 214 | 215 | if (n && w && !e && !s && se) return 10; 216 | if (n && s && !w && !e) return 18; 217 | if (!n && !s && w && e) return 32; 218 | if (n && w && e && !s) return 34; 219 | if (n && w && !e && s) return 21; 220 | if (!n && w && e && s) return 6; 221 | if (n && !w && e && s) return 19; 222 | 223 | if (!n && !e && s && w && !sw) return 11; 224 | if (!n && e && s && !w && !se) return 10; 225 | if (n && e && !s && !w && !ne) return 24; 226 | if (n && !e && !s && w && !nw) return 25; 227 | 228 | if (n && e && !s && !w) return 33; 229 | if (!n && e && s && !w) return 5; 230 | if (!n && !e && s && w) return 7; 231 | if (n && !e && !s && w) return 35; 232 | 233 | return 20; // fuck it 234 | }; -------------------------------------------------------------------------------- /public/styles/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0px; 3 | padding: 0px; 4 | background-color: #1f1d2c; 5 | text-align: center; 6 | font-size: 8px; 7 | font-family: "Press Start 2P"; 8 | color: #fff; 9 | } 10 | 11 | body > #gamefield { 12 | height: 448px; 13 | line-height: 448px; 14 | } 15 | 16 | body > #gamefield > span { 17 | font-size: 16px; 18 | } 19 | 20 | body > #gamefield > canvas { 21 | margin: 0 auto; 22 | } 23 | 24 | body > h1 { 25 | font-size: 24px; 26 | } 27 | 28 | body > #credits { 29 | margin-top: 8px; 30 | line-height: 12px; 31 | } 32 | 33 | a { 34 | color: #a1cad4; 35 | } 36 | 37 | body > #embed { 38 | display: none; 39 | } -------------------------------------------------------------------------------- /public/styles/progressjs.css: -------------------------------------------------------------------------------- 1 | .progressjs-inner { 2 | width: 0; 3 | } 4 | .progressjs-progress { 5 | z-index: 9999999; 6 | } 7 | 8 | /* blue theme, like iOS 7 progress bar */ 9 | .progressjs-theme-blue .progressjs-inner { 10 | height: 2px; 11 | -webkit-transition: all 0.3s ease-out; 12 | -moz-transition: all 0.3s ease-out; 13 | -o-transition: all 0.3s ease-out; 14 | transition: all 0.3s ease-out; 15 | background-color: #3498db; 16 | } 17 | .progressjs-theme-blue.progressjs-end { 18 | -webkit-transition: opacity 0.2s ease-out; 19 | -moz-transition: opacity 0.2s ease-out; 20 | -o-transition: opacity 0.2s ease-out; 21 | transition: opacity 0.2s ease-out; 22 | opacity: 0; 23 | } 24 | .progressjs-theme-blue .progressjs-percent { 25 | display: none; 26 | } 27 | 28 | .progressjs-theme-onslaught .progressjs-inner { 29 | height: 2px; 30 | -webkit-transition: all 0.3s ease-out; 31 | -moz-transition: all 0.3s ease-out; 32 | -o-transition: all 0.3s ease-out; 33 | transition: all 0.3s ease-out; 34 | background-color: #a1cad4; 35 | } 36 | .progressjs-theme-onslaught.progressjs-end { 37 | -webkit-transition: opacity 0.2s ease-out; 38 | -moz-transition: opacity 0.2s ease-out; 39 | -o-transition: opacity 0.2s ease-out; 40 | transition: opacity 0.2s ease-out; 41 | opacity: 0; 42 | } 43 | .progressjs-theme-onslaught .progressjs-percent { 44 | display: none; 45 | } 46 | 47 | /* blue theme with overlay layer, no percent bar */ 48 | .progressjs-theme-blueOverlay { 49 | background-color: white; 50 | -webkit-transition: all 0.2s ease-out; 51 | -moz-transition: all 0.2s ease-out; 52 | -o-transition: all 0.2s ease-out; 53 | transition: all 0.2s ease-out; 54 | } 55 | .progressjs-theme-blueOverlay .progressjs-inner { 56 | height: 100%; 57 | -webkit-transition: all 0.3s ease-out; 58 | -moz-transition: all 0.3s ease-out; 59 | -o-transition: all 0.3s ease-out; 60 | transition: all 0.3s ease-out; 61 | background-color: #3498db; 62 | } 63 | .progressjs-theme-blueOverlay.progressjs-end { 64 | opacity: 0 !important; 65 | } 66 | .progressjs-theme-blueOverlay .progressjs-percent { 67 | display: none; 68 | } 69 | 70 | /* blue theme with overlay layer, no percent bar */ 71 | .progressjs-theme-blueOverlay { 72 | background-color: white; 73 | -webkit-transition: all 0.2s ease-out; 74 | -moz-transition: all 0.2s ease-out; 75 | -o-transition: all 0.2s ease-out; 76 | transition: all 0.2s ease-out; 77 | } 78 | .progressjs-theme-blueOverlay .progressjs-inner { 79 | height: 100%; 80 | -webkit-transition: all 0.3s ease-out; 81 | -moz-transition: all 0.3s ease-out; 82 | -o-transition: all 0.3s ease-out; 83 | transition: all 0.3s ease-out; 84 | background-color: #3498db; 85 | } 86 | .progressjs-theme-blueOverlay.progressjs-end { 87 | opacity: 0 !important; 88 | } 89 | .progressjs-theme-blueOverlay .progressjs-percent { 90 | display: none; 91 | } 92 | 93 | /* Blue theme with border radius and overlay layer */ 94 | .progressjs-theme-blueOverlayRadius { 95 | background-color: white; 96 | -webkit-transition: all 0.2s ease-out; 97 | -moz-transition: all 0.2s ease-out; 98 | -o-transition: all 0.2s ease-out; 99 | transition: all 0.2s ease-out; 100 | border-radius: 5px; 101 | } 102 | .progressjs-theme-blueOverlayRadius .progressjs-inner { 103 | height: 100%; 104 | -webkit-transition: all 0.3s ease-out; 105 | -moz-transition: all 0.3s ease-out; 106 | -o-transition: all 0.3s ease-out; 107 | transition: all 0.3s ease-out; 108 | background-color: #3498db; 109 | border-radius: 5px; 110 | } 111 | .progressjs-theme-blueOverlayRadius.progressjs-end { 112 | opacity: 0 !important; 113 | } 114 | .progressjs-theme-blueOverlayRadius .progressjs-percent { 115 | display: none; 116 | } 117 | 118 | /* Blue theme with border radius and overlay layer */ 119 | .progressjs-theme-blueOverlayRadiusHalfOpacity { 120 | background-color: white; 121 | opacity: 0.5; 122 | -webkit-transition: all 0.2s ease-out; 123 | -moz-transition: all 0.2s ease-out; 124 | -o-transition: all 0.2s ease-out; 125 | transition: all 0.2s ease-out; 126 | border-radius: 5px; 127 | } 128 | .progressjs-theme-blueOverlayRadiusHalfOpacity .progressjs-inner { 129 | height: 100%; 130 | -webkit-transition: all 0.3s ease-out; 131 | -moz-transition: all 0.3s ease-out; 132 | -o-transition: all 0.3s ease-out; 133 | transition: all 0.3s ease-out; 134 | background-color: #3498db; 135 | border-radius: 5px; 136 | } 137 | .progressjs-theme-blueOverlayRadiusHalfOpacity.progressjs-end { 138 | opacity: 0 !important; 139 | } 140 | .progressjs-theme-blueOverlayRadiusHalfOpacity .progressjs-percent { 141 | display: none; 142 | } 143 | 144 | /* Blue theme with border radius, overlay layer and percent bar */ 145 | .progressjs-theme-blueOverlayRadiusWithPercentBar { 146 | background-color: white; 147 | -webkit-transition: all 0.2s ease-out; 148 | -moz-transition: all 0.2s ease-out; 149 | -o-transition: all 0.2s ease-out; 150 | transition: all 0.2s ease-out; 151 | border-radius: 5px; 152 | } 153 | .progressjs-theme-blueOverlayRadiusWithPercentBar .progressjs-inner { 154 | height: 100%; 155 | -webkit-transition: all 0.3s ease-out; 156 | -moz-transition: all 0.3s ease-out; 157 | -o-transition: all 0.3s ease-out; 158 | transition: all 0.3s ease-out; 159 | background-color: #3498db; 160 | border-radius: 5px; 161 | } 162 | .progressjs-theme-blueOverlayRadiusWithPercentBar.progressjs-end { 163 | opacity: 0 !important; 164 | } 165 | .progressjs-theme-blueOverlayRadiusWithPercentBar .progressjs-percent { 166 | width: 70px; 167 | text-align: center; 168 | height: 40px; 169 | position: absolute; 170 | right: 50%; 171 | margin-right: -35px; 172 | top: 50%; 173 | margin-top: -20px; 174 | font-size: 30px; 175 | opacity: .5; 176 | } 177 | 178 | .progressjs-theme-blackRadiusInputs { 179 | height: 10px; 180 | border-radius: 10px; 181 | overflow: hidden; 182 | } 183 | .progressjs-theme-blackRadiusInputs .progressjs-inner { 184 | height: 2px; 185 | -webkit-transition: all 1s ease-out; 186 | -moz-transition: all 1s ease-out; 187 | -o-transition: all 1s ease-out; 188 | transition: all 1s ease-out; 189 | background-color: #34495e; 190 | } 191 | .progressjs-theme-blackRadiusInputs.progressjs-end { 192 | -webkit-transition: opacity 0.2s ease-out; 193 | -moz-transition: opacity 0.2s ease-out; 194 | -o-transition: opacity 0.2s ease-out; 195 | transition: opacity 0.2s ease-out; 196 | opacity: 0; 197 | } 198 | .progressjs-theme-blackRadiusInputs .progressjs-percent { 199 | display: none; 200 | } --------------------------------------------------------------------------------