View complete gist here. ( Gist.github.com )
BlockStore.js
define(function(require, exports, module){
var _ = require('underscore');
var utils = require('client/utils');
var Phaser = require('phaser');
var Block = require('../actors/Block');
var PlanetGenerator = require('client/PlanetGenerator')
var constants = require('client/constants');
var EventEmitter = require('eventEmitter');
var BlockStore = function(options){
_.extend(this, {
api : null,
stores : {},
},options);
this.__shipsById = {};
this.__planetsById = {};
this.__planetsByBlockId = [];
this.__blocksIdByPlanetId = {};
this.__blocksById = [];
this.__blocksByPos = {};
this.__selectedBlocksHistory = [];
this.__onPlanetChangeBinded = this.__onPlanetChange.bind(this);
this.onBlockUpdated = new Phaser.Signal();
this.invalidateInBoundsBlocks();
this.generateEmptyBlocks();
var appDispatcher = options.appDispatcher;
this.dispatchToken = appDispatcher.register(this.onAppDispatcherAction.bind(this))
}
BlockStore.getBoundingBoxStepped = function(view, step){
// Adjusting so we don't care about negative values
var adjustX = 0;
if(view.x < 0){
adjustX = step * Math.ceil(-view.x/step);
}
view.x += adjustX;
var adjustY = 0;
if(view.y < 0){
adjustY = step * Math.ceil(-view.y/step);
}
view.y += adjustY;
var leftBoundingBox = view.x - view.x % step - adjustX;
var topBoundingBox = view.y - view.y % step - adjustY;
var horizontalSteps = Math.ceil((view.width + view.x % step) / step);
var verticalSteps = Math.ceil((view.height + view.y % step) / step);
var width = step * horizontalSteps;
var height = step * verticalSteps;
return {
x : leftBoundingBox,
y : topBoundingBox,
width : width,
height : height,
horizontalSteps : horizontalSteps,
verticalSteps : verticalSteps,
leftStep : leftBoundingBox / step,
topStep : topBoundingBox / step,
}
}
_.extend(BlockStore.prototype, EventEmitter.prototype, {
onAppDispatcherAction: function(payload){
var action = payload.action;
switch(action.type){
case 'players.unlockBlock':
// @todo.
break;
default:
// do nothing
}
},
selectBlock: function(block){
this.selectedBlock = block;
this.__selectedBlocksHistory.push(block);
},
getLastXSelectedBlocks: function(howMany){
return _.last(this.__selectedBlocksHistory, howMany)
},
getVisibleBlocks: function(){
var last2SelectedBlocks = this.getLastXSelectedBlocks(2);
console.log(last2SelectedBlocks.length)
return last2SelectedBlocks
},
updateAuthorizedBlocks: function(){
var unlockedBlocksIds = this.stores.authStore.currentPlayer.unlockedBlocksIds;
unlockedBlocksIds.forEach(function(blockId){
var block = this.getBlockById(blockId);
block.setAuthorized(true);
}.bind(this))
},
invalidateInBoundsBlocks: function(){
this.__inBoundsBlocksAreDirty = true;
},
updateInBoundsBlocks: function(){
if(!this.__inBoundsBlocksAreDirty) return;
// Use camera
var cameraVisibleRect = this.stores.cameraStore.getVisibleArea();
// Don't consider scale.
// The origin of the blocks is (0, 0)
var blockSize = constants.BLOCK_SIZE;
var boundingBoxStepped = BlockStore.getBoundingBoxStepped(cameraVisibleRect, blockSize);
var inBoundsBlocksIds = [];
var inBoundsBlocksById = {};
var inBoundsBlocks = [];
var block;
var currentPlayer = this.stores.authStore.currentPlayer;
for(var x = boundingBoxStepped.leftStep; x < boundingBoxStepped.leftStep + boundingBoxStepped.horizontalSteps; x++){
for(var y = boundingBoxStepped.topStep; y < boundingBoxStepped.topStep + boundingBoxStepped.verticalSteps; y++){
block = this.getBlockByXY(x, y);
// if(currentPlayer.unlockedBlocksIds.indexOf(block._id) === -1) continue;
inBoundsBlocksIds.push(block._id);
inBoundsBlocks.push(block);
inBoundsBlocksById[block._id] = block;
}
}
this.__inBoundsBlocksIds = inBoundsBlocksIds;
this.__inBoundsBlocksById = inBoundsBlocksById;
this.__inBoundsBlocks = inBoundsBlocks;
this.__inBoundsBlocksAreDirty = false;
},
getInBoundsBlocks: function(){
this.updateInBoundsBlocks();
return this.__inBoundsBlocks;
},
getInBoundsBlocksIds: function(){
this.updateInBoundsBlocks();
return this.__inBoundsBlocksIds;
},
getInBoundsBlocksById: function(){
this.updateInBoundsBlocks();
return this.__inBoundsBlocksById;
},
getRenderedBlocks: function(){
return this.__blocksById.filter(function(block){
return block.isRendered();
})
},
getOutOfBoundsRenderedBlocks: function(){
var renderedBlocks = this.getRenderedBlocks();
var authorizedAndInBoundsBlocks = this.getAuthorizedAndInBoundsBlocks();
var outOfBoundsRenderedBlocks = _.difference(renderedBlocks, authorizedAndInBoundsBlocks);
return outOfBoundsRenderedBlocks;
},
getAuthorizedAndInBoundsBlocks: function(){
return this.getInBoundsBlocks().filter(function(block){
return this.blockIdIsAuthorized(block._id);
}.bind(this))
},
blockIdIsAuthorized: function(blockId){
return this.stores.authStore.currentPlayer.unlockedBlocksIds.indexOf(blockId) !== -1;
},
generateEmptyBlocks: function(){
var i = 1000;
while(i--){
var blockPos = PlanetGenerator.getXYFromUlamSpiralIndex(i);
this.addBlock(new Block({
_id : i,
pos : blockPos,
game : this.game,
}))
}
},
getBlockByWorldPosXY: function(x, y){
var blockX = Math.floor(x / constants.BLOCK_SIZE);
var blockY = Math.floor(y / constants.BLOCK_SIZE);
return this.getBlockByXY(blockX, blockY);
},
getAllShips: function(){
var activeShips = [];
_.forEach(this.__shipsById, function(ship){
if(ship.destinationReached) return;
activeShips.push(ship);
})
return activeShips;
},
replaceShipId: function(oldShipId, newShipId){
var ship = this.__shipsById[oldShipId];
delete this.__shipsById[oldShipId];
this.__shipsById[newShipId] = ship;
ship._id = newShipId;
},
addShip: function(ship){
// Can happen when the ship travel between blocks because we
// get the ships by a certain block, therefore no need to render
// it again, as it's the exact same ship.
if(this.hasShip(ship)) return;
this.__shipsById[ship._id] = ship;
this.onBlockUpdated.dispatch(ship.from.block);
},
hasShip: function(ship){
return !!this.__shipsById[ship._id];
},
removeShipById: function(shipId){
var ship = this.__shipsById[shipId];
delete this.__shipsById[shipId];
this.onBlockUpdated.dispatch(ship.to.block);
},
__addPlanet: function(planet){
if(!planet) throw new Error('no planet')
var planetId = planet._id;
if(planetId === void 0) throw new Error('no planet id')
if(this.__planetsById[planetId]) throw new Error('planet already added')
this.__planetsById[planetId] = planet;
if(!this.__planetsByBlockId[planet.block.id]){
this.__planetsByBlockId[planet.block.id] = [];
}
this.__planetsByBlockId[planet.block.id].push(planet);
planet.onChange = this.__onPlanetChangeBinded;
},
__onPlanetChange: function(planet, what){
this.onBlockUpdated.dispatch(planet.block.id, what, planet);
},
setBlockPlanets: function(blockId, planets){
var _this = this;
planets.forEach(function(planet){
_this.__addPlanet(planet)
})
this.onBlockUpdated.dispatch(blockId);
},
getPlanetsInBlockId: function(blockId){
var planets = this.__planetsByBlockId[blockId];
return planets ? planets.slice() : false;
},
getPlanetById: function(planetId){
if(planetId === void 0) throw new Error('add a planet id')
return this.__planetsById[planetId];
},
getAllPlanets: function(){
return _.extend({}, this.__planetsById);
},
addBlock: function(block){
this.__blocksById[block._id] = block;
this.__blocksByPos[block.getPosAsString()] = block;
},
getBlockByXY: function(x, y){
return this.__blocksByPos[Block.getPosXYAsString(x, y)];
},
getBlockByPosString: function(posString){
return this.__blocksByPos[posString];
},
getBlockByPosObject: function(posObject){
return this.getBlockByPosString(Block.getPosObjectAsString(posObject));
},
getBlockById: function(id){
return this.__blocksById[id];
},
})
return BlockStore;
});