diff --git a/app/Bootstrap/HttpServer.js b/app/Bootstrap/HttpServer.js index b2a7183..e74a46a 100755 --- a/app/Bootstrap/HttpServer.js +++ b/app/Bootstrap/HttpServer.js @@ -65,6 +65,7 @@ function (http, nodeStatic, Api) { var status = self.api.isError ? 400 : 200; res.writeHead(status, {"Content-Type": self.api.getContentType()}); res.end(self.api.getOutput()); + self.api.isError = false; break; case new RegExp(/^\/app/).test(req.url): diff --git a/app/Game/Channel/Channel.js b/app/Game/Channel/Channel.js index ef099c7..5f39b08 100755 --- a/app/Game/Channel/Channel.js +++ b/app/Game/Channel/Channel.js @@ -13,30 +13,27 @@ var self = this; - this.options = options = Options.merge(options, { - levelUids: Settings.DEFAULT_LEVELS - }); - this.name = options.channelName; this.users = {}; - this.pipeToServer = pipeToServer; + this.levelListIndex = -1; - this.gameController = new GameController(this); - - + this.options = options = Options.merge(options, { + levelUids: Settings.CHANNEL_DEFAULT_LEVELS + }); + + // Notification Center + Nc.on(Nc.ns.channel.events.round.end, this.onEndRound, this); Nc.on(Nc.ns.channel.events.controlCommand.channel, function (message) { ProtocolHelper.applyCommand(message.data, self); }); - - Nc.on(Nc.ns.channel.to.client.gameCommand.broadcast, this.broadcastGameCommand, this); - - // prepared - not triggered yet + Nc.on(Nc.ns.channel.to.client.controlCommand.broadcast, this.broadcastControlCommand, this); //Nc.on(Nc.ns.channel.to.client.gameCommand.broadcastExcept, this.broadcastGameCommandExcept, this); - //Nc.on(Nc.ns.channel.to.client.controlCommand.broadcast, this.broadcastControlCommand, this); //Nc.on(Nc.ns.channel.to.client.controlCommand.broadcastExcept, this.broadcastControlCommandExcept, this); + this.beginRound(); + console.checkpoint('channel ' + this.name + ' created'); setTimeout(function() { @@ -46,19 +43,68 @@ }, Settings.CHANNEL_DESTRUCTION_TIME * 1000); } + Channel.prototype.getNextLevelUid = function() { + this.levelListIndex = (this.levelListIndex + 1) % this.options.levelUids.length; + return this.options.levelUids[this.levelListIndex]; + }; + + + Channel.prototype.beginRound = function() { + + if(this.gameController) { + this.gameController.destroy(); + delete this.gameController; + } + + var gameControllerOptions = { + channelName: this.name, + scoreLimit: this.options.scoreLimit, + levelUid: this.getNextLevelUid() + }; + + console.log(gameControllerOptions) + + this.gameController = new GameController(gameControllerOptions); + + for(var userId in this.users) { + this.gameController.createPlayer(this.users[userId]); + } + + var clientGameControllerOptions = { + levelUid: gameControllerOptions.levelUid + }; + + this.broadcastControlCommand("beginRound", clientGameControllerOptions); + }; + + Channel.prototype.onEndRound = function() { + var self = this; + this.broadcastControlCommand("endRound", true); + + setTimeout(function() { + self.beginRound(); + }, Settings.CHANNEL_END_ROUND_TIME * 1000); + }; + // Channel command callbacks Channel.prototype.onAddUser = function (options) { var self = this; + var clientGameControllerOptions = { + levelUid: this.gameController.options.levelUid + }; + if(!this.gameController.level || !this.gameController.level.isLoaded) { var token = Nc.on(Nc.ns.core.game.events.level.loaded, function() { self.sendJoinSuccess(options); + this.users[options.id].sendControlCommand("beginRound", clientGameControllerOptions); Nc.off(token); }); } else { self.sendJoinSuccess(options); + this.users[options.id].sendControlCommand("beginRound", clientGameControllerOptions); } } @@ -91,15 +137,24 @@ }; Channel.prototype.onReleaseUser = function (userId) { + var self = this; var user = this.users[userId]; Nc.trigger(Nc.ns.channel.events.user.left, userId); delete this.users[userId]; this.broadcastControlCommand("userLeft", userId); - // FIXME: if this was the last user terminate forked process if(Object.keys(this.users).length < 1) { - this.destroy(); + + console.checkpoint("channel (" + this.name + ") destruction scheduled. t - " + Settings.CHANNEL_DESTRUCTION_TIME + " seconds"); + + setTimeout(function() { + if(Object.keys(self.users).length < 1) { + self.destroy(); + } else { + console.checkpoint("channel (" + self.name + ") destruction aborted (a user joined)."); + } + }, Settings.CHANNEL_DESTRUCTION_TIME * 1000); } } diff --git a/app/Game/Channel/GameController.js b/app/Game/Channel/GameController.js index bda7b00..df17d06 100755 --- a/app/Game/Channel/GameController.js +++ b/app/Game/Channel/GameController.js @@ -14,32 +14,34 @@ define([ function (Parent, PhysicsEngine, Settings, PlayerController, requestAnimFrame, Nc, Box2D, Player, GameObject, Doll, RagDoll) { - function GameController (channel) { - - this.channel = channel; + function GameController (options) { - Parent.call(this); + this.animationTimeout = null; + this.worldUpdateTimeout = null; + this.spawnTimeouts = []; - Nc.on(Nc.ns.channel.events.user.joined, this.onUserJoined, this); - Nc.on(Nc.ns.channel.events.user.left, this.onUserLeft, this); - Nc.on(Nc.ns.channel.events.user.level.reset, this.onResetLevel, this); - Nc.on(Nc.ns.channel.events.user.client.ready, this.onClientReady, this); + Parent.call(this, options); - Nc.on(Nc.ns.core.game.player.killed, this.onPlayerKilled, this); + this.ncTokens = this.ncTokens.concat([ + Nc.on(Nc.ns.channel.events.user.joined, this.onUserJoined, this), + Nc.on(Nc.ns.channel.events.user.left, this.onUserLeft, this), + Nc.on(Nc.ns.channel.events.user.level.reset, this.onResetLevel, this), + Nc.on(Nc.ns.channel.events.user.client.ready, this.onClientReady, this), + Nc.on(Nc.ns.core.game.events.level.loaded, this.onLevelLoaded, this), + Nc.on(Nc.ns.core.game.player.killed, this.onPlayerKilled, this), // FIXME: move to events + ]); - console.checkpoint('starting game controller for channel ' + channel.name); - - var nextUid = this.getNextLevelUid(); - this.loadLevel(nextUid); + console.checkpoint('starting game controller for channel (' + options.channelName + ')'); } + GameController.prototype = Object.create(Parent.prototype); GameController.prototype.update = function () { Parent.prototype.update.call(this); - requestAnimFrame(this.update.bind(this)); + this.animationTimeout = requestAnimFrame(this.update.bind(this)); this.physicsEngine.update(); for(var id in this.players) { @@ -48,7 +50,6 @@ function (Parent, PhysicsEngine, Settings, PlayerController, requestAnimFrame, N } GameController.prototype.onLevelLoaded = function() { - Parent.prototype.onLevelLoaded.call(this); this.updateWorld(); }; @@ -62,8 +63,11 @@ function (Parent, PhysicsEngine, Settings, PlayerController, requestAnimFrame, N user.setPlayer(player); }; - GameController.prototype.onPlayerKilled = function(player, respawnTime) { - this.spawnPlayer(player, respawnTime); + GameController.prototype.onPlayerKilled = function(player, killedByPlayer) { + if(killedByPlayer.stats.score >= this.options.scoreLimit) { + Nc.trigger(Nc.ns.channel.events.round.end); + } + this.spawnPlayer(player, Settings.RESPAWN_TIME); }; GameController.prototype.spawnPlayer = function(player, respawnTime) { @@ -74,7 +78,7 @@ function (Parent, PhysicsEngine, Settings, PlayerController, requestAnimFrame, N ? Settings.RESPAWN_TIME : respawnTime; - setTimeout(function() { + var spawnTimeout = setTimeout(function() { player.spawn(spawnPoint.x, spawnPoint.y); // put it into self.gameObjects.animated.push(player); @@ -86,7 +90,13 @@ function (Parent, PhysicsEngine, Settings, PlayerController, requestAnimFrame, N }; Nc.trigger(Nc.ns.channel.to.client.gameCommand.broadcast, "spawnPlayer", options); + + var i = self.spawnTimeouts.indexOf(spawnTimeout); + self.spawnTimeouts.splice(i, 1); + }, respawnTime * 1000); + + this.spawnTimeouts.push(spawnTimeout); }; GameController.prototype.updateWorld = function () { @@ -97,7 +107,7 @@ function (Parent, PhysicsEngine, Settings, PlayerController, requestAnimFrame, N Nc.trigger(Nc.ns.channel.to.client.gameCommand.broadcast, 'worldUpdate', update); } - setTimeout(this.updateWorld.bind(this), Settings.WORLD_UPDATE_BROADCAST_INTERVAL); + this.worldUpdateTimeout = setTimeout(this.updateWorld.bind(this), Settings.WORLD_UPDATE_BROADCAST_INTERVAL); } GameController.prototype.getWorldUpdateObject = function(getSleeping) { @@ -196,23 +206,16 @@ function (Parent, PhysicsEngine, Settings, PlayerController, requestAnimFrame, N } }; - GameController.prototype.getNextLevelUid = function() { - if(!this.level) return this.channel.options.levelUids[0]; + GameController.prototype.destroy = function() { + clearTimeout(this.animationTimeout); + clearTimeout(this.worldUpdateTimeout); - var levelCount = this.channel.options.levelUids.length; - - for (var i = 0; i < levelCount; i++) { - var uid = this.channel.options.levelUids[i]; - if(uid == this.level.uid) { - break; - } + for (var i = 0; i < this.spawnTimeouts.length; i++) { + clearTimeout(this.spawnTimeouts[i]); }; - var next = i + 1; - - return this.channel.options.levelUids[next % levelCount]; + Parent.prototype.destroy.call(this); }; - return GameController; }); diff --git a/app/Game/Channel/GameObjects/Items/RagDoll.js b/app/Game/Channel/GameObjects/Items/RagDoll.js index 0f19f47..37f8074 100644 --- a/app/Game/Channel/GameObjects/Items/RagDoll.js +++ b/app/Game/Channel/GameObjects/Items/RagDoll.js @@ -30,15 +30,21 @@ function (Parent, Settings, Nc) { }; RagDoll.prototype.delayedDestroy = function() { + var self = this; this.scheduledForDestruction = true; - this.destructionTimeout = setTimeout(this.destroy.bind(this), Settings.RAGDOLL_DESTRUCTION_TIME * 1000); + this.destructionTimeout = setTimeout(function() { + Nc.trigger(Nc.ns.channel.to.client.gameCommand.broadcast, 'removeGameObject', { + type: 'animated', + uid: self.uid + }); + self.destroy.bind(self); + }, Settings.RAGDOLL_DESTRUCTION_TIME * 1000); }; RagDoll.prototype.destroy = function() { - Nc.trigger(Nc.ns.channel.to.client.gameCommand.broadcast, 'removeGameObject', { - type: 'animated', - uid: this.uid - }); + if(this.scheduledForDestruction) { + clearTimeout(this.destructionTimeout); + } Parent.prototype.destroy.call(this); }; diff --git a/app/Game/Client/GameController.js b/app/Game/Client/GameController.js index 23a5423..c762350 100755 --- a/app/Game/Client/GameController.js +++ b/app/Game/Client/GameController.js @@ -14,21 +14,27 @@ define([ function (Parent, Box2D, PhysicsEngine, ViewManager, PlayerController, Nc, requestAnimFrame, Settings, GameObject, Doll, DomController) { - function GameController () { + if (!window.cancelAnimationFrame) { + window.cancelAnimationFrame = function(id) { + clearTimeout(id); + }; + } + + + function GameController (options) { this.view = ViewManager.createView(); this.me = null; + this.animationRequestId = null; - Nc.on(Nc.ns.client.game.gameInfo.toggle, this.toggleInfo, this); + Parent.call(this, options); - Parent.call(this); + this.ncTokens = this.ncTokens.concat([ + Nc.on(Nc.ns.client.game.gameInfo.toggle, this.toggleInfo, this) + ]); } GameController.prototype = Object.create(Parent.prototype); - GameController.prototype.destruct = function() { - //destroy box2d world etc. - }; - GameController.prototype.getMe = function () { return this.me; } @@ -39,7 +45,7 @@ function (Parent, Box2D, PhysicsEngine, ViewManager, PlayerController, Nc, reque DomController.statsBegin(); - requestAnimFrame(this.update.bind(this)); + this.animationRequestId = requestAnimFrame(this.update.bind(this)); this.physicsEngine.update(); @@ -82,7 +88,7 @@ function (Parent, Box2D, PhysicsEngine, ViewManager, PlayerController, Nc, reque if(!alreadyExists) { var item = this.level.createItem(itemDef.uid, itemDef.options); - this.onGameObjectAdd("animated", item); + //this.onGameObjectAdd("animated", item); } }; } @@ -189,7 +195,7 @@ function (Parent, Box2D, PhysicsEngine, ViewManager, PlayerController, Nc, reque } } if(object) { - this.onGameObjectRemove(options.type, object); + //this.onGameObjectRemove(options.type, object); object.destroy(); } else { console.warn("GameObject for removal can not be found locally. " + options.uid); @@ -253,5 +259,15 @@ function (Parent, Box2D, PhysicsEngine, ViewManager, PlayerController, Nc, reque this.view.toggleInfo(show, string); }; + GameController.prototype.destroy = function() { + + cancelAnimationFrame(this.animationRequestId); + + Parent.prototype.destroy.call(this); + + this.view.render(); + this.view.test(); + }; + return GameController; }); diff --git a/app/Game/Client/GameObjects/Items/RagDoll.js b/app/Game/Client/GameObjects/Items/RagDoll.js index 9bf5c46..f89c72b 100644 --- a/app/Game/Client/GameObjects/Items/RagDoll.js +++ b/app/Game/Client/GameObjects/Items/RagDoll.js @@ -99,6 +99,9 @@ function (Parent, CoreItem, Settings, Nc) { }; RagDoll.prototype.destroy = function() { + + console.log('ragdoll destroy'); + for (var name in this.limbMeshes) { Nc.trigger(Nc.ns.client.view.mesh.remove, this.limbMeshes[name]); }; diff --git a/app/Game/Client/Networker.js b/app/Game/Client/Networker.js index 0bf9258..5305c85 100755 --- a/app/Game/Client/Networker.js +++ b/app/Game/Client/Networker.js @@ -48,8 +48,8 @@ function (ProtocolHelper, GameController, User, Nc, Settings, DomController) { } Networker.prototype.onDisconnect = function () { - if(this.gameController) this.gameController.destruct(); - this.gameController = null; + //if(this.gameController) this.gameController.destruct(); + //this.gameController = null; console.log('disconnected. game destroyed. no auto-reconnect'); document.body.style.backgroundColor = '#aaaaaa'; } @@ -57,9 +57,6 @@ function (ProtocolHelper, GameController, User, Nc, Settings, DomController) { Networker.prototype.onJoinSuccess = function (options) { console.log("join success") - this.gameController = new GameController(); - this.gameController.loadLevel(options.levelUid); - this.onUserJoined(options.user); if (options.joinedUsers) { @@ -140,6 +137,21 @@ function (ProtocolHelper, GameController, User, Nc, Settings, DomController) { setTimeout(this.ping.bind(this), 1000); }; + Networker.prototype.onBeginRound = function(options) { + + if(this.gameController) { + this.gameController.destroy(); + delete this.gameController; + } + + console.log('GameController') + this.gameController = new GameController(options); + }; + + Networker.prototype.onEndRound = function() { + this.gameController.toggleInfo(true); + }; + return Networker; }); \ No newline at end of file diff --git a/app/Game/Client/Player.js b/app/Game/Client/Player.js index a5419d5..e11dc6b 100755 --- a/app/Game/Client/Player.js +++ b/app/Game/Client/Player.js @@ -101,8 +101,9 @@ function (Parent, Nc, Settings) { }; Player.prototype.destroy = function() { - Parent.prototype.destroy.call(this); + clearTimeout(this.playerInfoViewVisibleTimeout); Nc.trigger(Nc.ns.client.view.playerInfo.remove, this.playerInfoView); + Parent.prototype.destroy.call(this); }; return Player; diff --git a/app/Game/Client/View/Views/PixiView.js b/app/Game/Client/View/Views/PixiView.js index 09bedd0..27439f0 100755 --- a/app/Game/Client/View/Views/PixiView.js +++ b/app/Game/Client/View/Views/PixiView.js @@ -32,7 +32,7 @@ function (Parent, DomController, PIXI, Settings, Nc) { var transparent = false; var antialias = true; - var canvas = null; // lets pixi create one + var canvas = DomController.getCanvas(); if(Settings.USE_WEBGL) { this.renderer = new PIXI.WebGLRenderer(Settings.STAGE_WIDTH, Settings.STAGE_HEIGHT, canvas, transparent, antialias); @@ -42,7 +42,7 @@ function (Parent, DomController, PIXI, Settings, Nc) { console.log('CanvasRenderer - not using WebGL!') } - this.stage = new PIXI.Stage(0x333333); + this.stage = new PIXI.Stage(/*0x333333*/); this.initCamera(); this.initInfo(); @@ -305,5 +305,9 @@ function (Parent, DomController, PIXI, Settings, Nc) { this.loader.visible = progress < 100; }; + PixiView.prototype.test = function() { + console.log(this.stage.children) + }; + return PixiView; }); diff --git a/app/Game/Config/Settings.js b/app/Game/Config/Settings.js index ce32adf..0734016 100755 --- a/app/Game/Config/Settings.js +++ b/app/Game/Config/Settings.js @@ -18,7 +18,6 @@ define(function() { GRAPHICS_SUBPATH_CHARACTERS: 'Characters/', GRAPHICS_SUBPATH_TILES: 'Tiles/', MAPS_PATH: 'static/maps/tiled/', - DEFAULT_LEVELS: ['stones2', 'debug', 'stones2', 'debug'], RATIO: 21, //35 // original tile size is 25 but we want it to resize to 20 @@ -37,9 +36,9 @@ define(function() { MAX_THROW_FORCE: 18 * 3.5, MAX_THROW_ANGULAR_VELOCITY: 0, MAX_RUNNING_WEIGHT: 9, - RESPAWN_TIME: 6, + RESPAWN_TIME: 512, HEALTH_DISPLAY_TIME: 2, - RAGDOLL_DESTRUCTION_TIME: 25, + RAGDOLL_DESTRUCTION_TIME: 250, // restitution: bouncyness, friction: rubbing, density: mass TILE_FRICTION: 0.99, @@ -65,7 +64,13 @@ define(function() { WORLD_UPDATE_BROADCAST_INTERVAL: 70, CHANNEL_DESTRUCTION_TIME: 30, NETWORK_LOG_INCOMING: false, - NETWORK_LOG_OUTGOING: false + NETWORK_LOG_OUTGOING: false, + + // CHANNEL + CHANNEL_END_ROUND_TIME: 10, + CHANNEL_DEFAULT_MAX_USERS: 40, + CHANNEL_DEFAULT_SCORE_LIMIT: 10, + CHANNEL_DEFAULT_LEVELS: ['stones2', 'debug', 'stones2', 'debug'] } Settings.TILE_RATIO = Settings.ORIGINAL_TILE_SIZE / Settings.TILE_SIZE; diff --git a/app/Game/Core/GameController.js b/app/Game/Core/GameController.js index 2fac437..c83c1ca 100755 --- a/app/Game/Core/GameController.js +++ b/app/Game/Core/GameController.js @@ -7,7 +7,9 @@ define([ function (PhysicsEngine, TiledLevel, Player, Nc) { - function GameController () { + function GameController (options) { + + this.options = options; this.players = {}; this.level = null; this.gameObjects = null; @@ -16,10 +18,12 @@ function (PhysicsEngine, TiledLevel, Player, Nc) { this.physicsEngine = new PhysicsEngine(); this.physicsEngine.setCollisionDetector(); - Nc.on(Nc.ns.core.game.events.level.loaded, this.onLevelLoaded, this); + this.ncTokens = [ + Nc.on(Nc.ns.core.game.gameObject.add, this.onGameObjectAdd, this), + Nc.on(Nc.ns.core.game.gameObject.remove, this.onGameObjectRemove, this) + ]; - Nc.on(Nc.ns.core.game.gameObject.add, this.onGameObjectAdd, this); - Nc.on(Nc.ns.core.game.gameObject.remove, this.onGameObjectRemove, this); + this.loadLevel(options.levelUid); this.update(); } @@ -62,15 +66,28 @@ function (PhysicsEngine, TiledLevel, Player, Nc) { this.loadLevel(this.level.uid); }; - GameController.prototype.onLevelLoaded = function() { - - }; - - GameController.prototype.destroy = function () { for(var player in this.players) { - this.players[player].destroy(); + // this.players[player].destroy(); + + // FIXME: + // commented out for now, because players are in gameObjects array. + // try using a real gameobject for the health bar } + + for (var i = 0; i < this.ncTokens.length; i++) { + Nc.off(this.ncTokens[i]); + }; + + for (var key in this.gameObjects) { + for (var i = 0; i < this.gameObjects[key].length; i++) { + var gameObject = this.gameObjects[key][i]; + this.onGameObjectRemove(key, gameObject); + gameObject.destroy(); + }; + }; + + this.physicsEngine.destroy(); } /* diff --git a/app/Game/Core/GameObjects/Doll.js b/app/Game/Core/GameObjects/Doll.js index f7ddde6..6f2e89e 100755 --- a/app/Game/Core/GameObjects/Doll.js +++ b/app/Game/Core/GameObjects/Doll.js @@ -3,10 +3,11 @@ define([ "Lib/Vendor/Box2D", "Game/Config/Settings", "Game/" + GLOBALS.context + "/Collision/Detector", - "Game/" + GLOBALS.context + "/GameObjects/Item" + "Game/" + GLOBALS.context + "/GameObjects/Item", + "Lib/Utilities/NotificationCenter" ], -function (Parent, Box2D, Settings, CollisionDetector, Item) { +function (Parent, Box2D, Settings, CollisionDetector, Item, Nc) { function Doll (physicsEngine, uid, player) { @@ -36,6 +37,8 @@ function (Parent, Box2D, Settings, CollisionDetector, Item) { this.createFixtures(); this.body.SetActive(false); + + Nc.trigger(Nc.ns.core.game.gameObject.add, 'animated', this); } Doll.prototype = Object.create(Parent.prototype); @@ -368,5 +371,9 @@ function (Parent, Box2D, Settings, CollisionDetector, Item) { } }; + Doll.prototype.destroy = function() { + Nc.trigger(Nc.ns.core.game.gameObject.remove, 'animated', this); + }; + return Doll; }); \ No newline at end of file diff --git a/app/Game/Core/GameObjects/Item.js b/app/Game/Core/GameObjects/Item.js index a04bac2..dea0e5f 100644 --- a/app/Game/Core/GameObjects/Item.js +++ b/app/Game/Core/GameObjects/Item.js @@ -3,10 +3,11 @@ define([ "Lib/Vendor/Box2D", "Lib/Utilities/Options", "Game/Config/Settings", - "Lib/Utilities/Exception" + "Lib/Utilities/Exception", + "Lib/Utilities/NotificationCenter" ], -function (Parent, Box2D, Options, Settings, Exception) { +function (Parent, Box2D, Options, Settings, Exception, Nc) { function Item(physicsEngine, uid, options) { @@ -33,6 +34,8 @@ function (Parent, Box2D, Options, Settings, Exception) { this.createFixture(); this.body.ResetMassData(); this.flipDirection = 1; + + Nc.trigger(Nc.ns.core.game.gameObject.add, 'animated', this); } Item.prototype = Object.create(Parent.prototype); @@ -136,6 +139,11 @@ function (Parent, Box2D, Options, Settings, Exception) { body.SetAngularVelocity(Settings.MAX_THROW_ANGULAR_VELOCITY * x); }; + + Item.prototype.destroy = function() { + Nc.trigger(Nc.ns.core.game.gameObject.remove, 'animated', this); + Parent.prototype.destroy.call(this); + }; return Item; diff --git a/app/Game/Core/GameObjects/Tile.js b/app/Game/Core/GameObjects/Tile.js index 3948274..2f483db 100755 --- a/app/Game/Core/GameObjects/Tile.js +++ b/app/Game/Core/GameObjects/Tile.js @@ -2,15 +2,18 @@ define([ "Game/" + GLOBALS.context + "/GameObjects/GameObject", "Lib/Vendor/Box2D", "Game/Config/Settings", - "Lib/Utilities/Exception" + "Lib/Utilities/Exception", + "Lib/Utilities/NotificationCenter" ], -function (Parent, Box2D, Settings, Exception) { +function (Parent, Box2D, Settings, Exception, Nc) { function Tile(physicsEngine, uid, options) { this.options = options; Parent.call(this, physicsEngine, uid); this.createPhysicTile(this.options); + + Nc.trigger(Nc.ns.core.game.gameObject.add, 'fixed', this); } Tile.prototype = Object.create(Parent.prototype); @@ -112,6 +115,10 @@ function (Parent, Box2D, Settings, Exception) { Tile.prototype.addVec = function (vs, m1, m2) { return vs.push(new Box2D.Common.Math.b2Vec2(this.mkArg(m1), this.mkArg(m2))); } + + Tile.prototype.destroy = function() { + Nc.trigger(Nc.ns.core.game.gameObject.remove, 'fixed', this); + }; return Tile; diff --git a/app/Game/Core/Loader/Level.js b/app/Game/Core/Loader/Level.js index 3e2d497..2709343 100755 --- a/app/Game/Core/Loader/Level.js +++ b/app/Game/Core/Loader/Level.js @@ -10,11 +10,10 @@ define([ ], function (Settings, Box2D, Nc, CollisionDetector, Tile, Item, Skateboard, RagDoll) { - function Level (uid, engine, gameObjects) { + function Level (uid, engine) { this.uid = uid; this.engine = engine; this.levelObject = null; - this.gameObjects = gameObjects; this.isLoaded = false; this.load(this.uid); } @@ -32,11 +31,13 @@ define([ } Level.prototype.destroy = function () { + /* for (var key in this.gameObjects) { for (var i = 0; i < this.gameObjects[key].length; i++) { this.gameObjects[key][i].destroy(); } } + */ this.isLoaded = false; } @@ -52,7 +53,9 @@ define([ var options = tiles[i]; //options.m = this.tileAtPositionExists(options.x, options.y - 1) ? "Soil" : "GrassSoil"; options.m = "Soil"; - this.gameObjects.fixed.push(new Tile(this.engine, "tile-" + i, options)); + //this.gameObjects.fixed.push( + new Tile(this.engine, "tile-" + i, options); + //); } } @@ -66,7 +69,7 @@ define([ var options = items[i]; var uid = "item-" + i; var item = this.createItem(uid, options); - this.gameObjects.animated.push(item); // FIXME: use Nc + //this.gameObjects.animated.push(item); }; }; diff --git a/app/Game/Core/Loader/TiledLevel.js b/app/Game/Core/Loader/TiledLevel.js index baf4d95..9ffe666 100755 --- a/app/Game/Core/Loader/TiledLevel.js +++ b/app/Game/Core/Loader/TiledLevel.js @@ -13,10 +13,10 @@ define([ ], function (Parent, Settings, ItemSettings, Box2D, Options, Exception, CollisionDetector, Tile, Item, Skateboard) { // Public - function TiledLevel (path, engine, gameObjects) { + function TiledLevel (path, engine) { this.levelData = null; - Parent.call(this, path, engine, gameObjects); + Parent.call(this, path, engine); } TiledLevel.prototype = Object.create(Parent.prototype); @@ -51,7 +51,9 @@ define([ y: parseInt(i / collisionLayer.height , 10) } - this.gameObjects.fixed.push(new Tile(this.engine, "tile-" + i, options)); + //this.gameObjects.fixed.push( + new Tile(this.engine, "tile-" + i, options); + //); } } else { @@ -69,7 +71,7 @@ define([ var uid = "item-" + i; var item = this.createItem(uid, options); - this.gameObjects.animated.push(item); + //this.gameObjects.animated.push(item); }; }; diff --git a/app/Game/Core/Physics/Engine.js b/app/Game/Core/Physics/Engine.js index e6945e4..911fc1b 100755 --- a/app/Game/Core/Physics/Engine.js +++ b/app/Game/Core/Physics/Engine.js @@ -60,6 +60,10 @@ function (Settings, Box2D, CollisionDetector, Nc) { this.processWorldQueue(); } + Engine.prototype.destroy = function() { + delete this.world; + }; + return Engine; }); \ No newline at end of file diff --git a/app/Game/Core/Player.js b/app/Game/Core/Player.js index 50e0f9c..6e52474 100755 --- a/app/Game/Core/Player.js +++ b/app/Game/Core/Player.js @@ -40,7 +40,6 @@ function (Doll, Settings, Nc, Exception, SpectatorDoll, RagDoll) { this.doll = new Doll(this.physicsEngine, "doll-" + this.id, this); this.doll.spawn(x, y); this.isSpawned = true; - Nc.trigger(Nc.ns.core.game.gameObject.add, 'animated', this.doll); } Player.prototype.getPosition = function () { @@ -120,14 +119,13 @@ function (Doll, Settings, Nc, Exception, SpectatorDoll, RagDoll) { this.isSpawned = false; - Nc.trigger(Nc.ns.core.game.gameObject.remove, 'animated', this.doll); this.doll.destroy(); this.doll = null; this.ragDoll = ragDoll; - Nc.trigger(Nc.ns.core.game.player.killed, this); + Nc.trigger(Nc.ns.core.game.player.killed, this, killedByPlayer); }; Player.prototype.update = function () { @@ -148,7 +146,6 @@ function (Doll, Settings, Nc, Exception, SpectatorDoll, RagDoll) { this.spectatorDoll.destroy(); if(this.doll) this.doll.destroy(); - if(this.ragDoll) this.ragDoll.destroy(); } Player.prototype.setPlayerController = function(playerController) { diff --git a/app/Lib/Utilities/NotificationCenter.js b/app/Lib/Utilities/NotificationCenter.js index 13be863..343321b 100755 --- a/app/Lib/Utilities/NotificationCenter.js +++ b/app/Lib/Utilities/NotificationCenter.js @@ -104,6 +104,9 @@ function (Exception) { level: { reset: null } + }, + round: { + end: null } }, engine: { @@ -128,6 +131,9 @@ function (Exception) { }, gameCommand: { broadcast: null + }, + controlCommand: { + broadcast: null } } } diff --git a/app/Lib/Utilities/RequestAnimFrame.js b/app/Lib/Utilities/RequestAnimFrame.js index e961c26..2c849c4 100755 --- a/app/Lib/Utilities/RequestAnimFrame.js +++ b/app/Lib/Utilities/RequestAnimFrame.js @@ -7,7 +7,7 @@ function (Settings) { var requestAnimFrame = (function () { var _setTimeout = function ( callback ) { - setTimeout(callback, Settings.BOX2D_TIME_STEP * 1000); + return setTimeout(callback, Settings.BOX2D_TIME_STEP * 1000); } if (typeof window != 'undefined') { diff --git a/app/Lib/Utilities/Validate.js b/app/Lib/Utilities/Validate.js new file mode 100644 index 0000000..4e2eb1a --- /dev/null +++ b/app/Lib/Utilities/Validate.js @@ -0,0 +1,61 @@ +define([ +], + +function () { + + function validate(object, description) { + + if(description.optional && (object === null || object === "")) { + return true; + } + + if(object === null) { + return false; + } + + if(typeof object === 'undefined') { + return false; + } + + if(typeof object === 'NaN') { + return false; + } + + if(description.type == 'array' && !(object instanceof Array)) { + return false; + } + + if(description.type != 'array' && typeof object != description.type) { + return false; + } + + if(description.max && object > description.max) { + return false; + } + + if(description.min && object < description.min) { + return false; + } + + if(description.maxLength && object.length > description.maxLength) { + return false; + } + + if(description.minLength && object.length < description.minLength) { + return false; + } + + if(description.in && description.in.indexOf(object) === -1) { + return false; + } + + if(description.regex && !description.regex.test(object)) { + return false; + } + + return true; + } + + return validate; + +}); \ No newline at end of file diff --git a/app/Server/Api.js b/app/Server/Api.js index 846deb5..23bb3cf 100644 --- a/app/Server/Api.js +++ b/app/Server/Api.js @@ -1,9 +1,12 @@ define([ "Lib/Utilities/NotificationCenter", - "Lib/Utilities/Protocol/Helper" + "Lib/Utilities/Protocol/Helper", + "Lib/Utilities/Validate", + "Lib/Utilities/Options", + "Game/Config/Settings" ], -function (Nc, ProtocolHelper) { +function (Nc, ProtocolHelper, validate, Options, Settings) { function Api(coordinator) { this.coordinator = coordinator; @@ -14,15 +17,16 @@ function (Nc, ProtocolHelper) { Api.prototype.handleCall = function(queryParameters) { var command, - output = null; + output = null; try { var message = JSON.parse(queryParameters); command = message.command; } catch(e) { this.isError = true; - output = "JSON syntax error"; - console.error(e) + this.output = "JSON syntax error"; + console.error(e); + return; } switch(command) { @@ -43,6 +47,74 @@ function (Nc, ProtocolHelper) { } Api.prototype.createChannel = function(options) { + + var allowedOptionKeys = [ + "channelName", + "maps", + "maxUsers", + "minUsers", + "scoreLimit" + ]; + + var newOptions = {}; + + // Channelname + if(validate(options.channelName, {type: 'string', minLength: 3, maxLength: 30})) { + newOptions.channelName = options.channelName; + } else { + this.isError = true; + return "Could not create channel, invalid channel name."; + } + + // Maps / levelUids + if( ! validate(options.levelUids, {type: 'array', minLength: 1, maxLength: 999})) { + this.isError = true; + return "Could not create channel, invalid maps."; + } + + for(var i = 0; i < options.levelUids.length; i++) { + if(!validate(options.levelUids[i], {type: 'string', in: ['stones2', 'debug']})) { + this.isError = true; + return "Could not create channel, invalid map (" + options.levelUids[i] + ")."; + } + } + + newOptions.levelUids = options.levelUids; + + // Users + if(validate(options.maxUsers, {optional: true, type: 'number', min: 1, max: Settings.CHANNEL_MAX_USERS})) { + newOptions.maxUsers = options.maxUsers; + } else { + this.isError = true; + return "Could not create channel, Max users invalid. (Limited to " + Settings.CHANNEL_MAX_USERS + " users)"; + } + + if(validate(options.minUsers, {optional: true, type: 'number', min: 0, max: Settings.CHANNEL_MAX_USERS})) { + newOptions.minUsers = options.minUsers; + } else { + this.isError = true; + return "Could not create channel, Max users too high. Limited to: " + Settings.CHANNEL_MAX_USERS; + } + + // Limits + if(validate(options.scoreLimit, {type: 'number', min: 1, max: 999})) { + newOptions.scoreLimit = options.scoreLimit; + } else { + this.isError = true; + return "Could not create channel, score limit (" + options.scoreLimit + ")."; + } + + + + var defaultOptions = { + maxUsers: Settings.CHANNEL_DEFAULT_MAX_USERS, + minUsers: 0, + scoreLimit: Settings.CHANNEL_DEFAULT_SCORE_LIMIT + }; + + options = Options.merge(options, defaultOptions); + + var result = this.coordinator.createChannel(options); if(result !== false) { return result; @@ -56,6 +128,11 @@ function (Nc, ProtocolHelper) { var output = {}; var key = this.isError ? "error" : "success"; output[key] = this.output; + + if(this.isError) { + console.warn("API Error: " + this.output); + } + return JSON.stringify(output); }; @@ -63,6 +140,8 @@ function (Nc, ProtocolHelper) { Api.prototype.getContentType = function() { return "application/json"; }; + + return Api; diff --git a/static/js/menu.js b/static/js/menu.js index 79e57a8..42dcebd 100644 --- a/static/js/menu.js +++ b/static/js/menu.js @@ -185,6 +185,7 @@ function validateForJoin(nickname, channelName) { } function validateForCreate(channelName, maps) { + return true; if(maps.length < 1) { alert("Please choose at least one map.") return false; @@ -243,9 +244,10 @@ function create(channelName, callback) { var options = { channelName: channelName, - maps: maps, + levelUids: maps, maxUsers: 10, - minUsers: 2 + minUsers: 2, + scoreLimit: 1 } localStorage["customname"] = channelName;