Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Planets Multiplayer: Source code
- Game: http://54.77.31.219/
- Last version video: https://www.youtube.com/watch?v=5H3Pu4WFZD4
- Community: https://plus.google.com/u/1/communities/105437932515232469714
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;
});
define(function(require, exports, module){
var _ = require('underscore');
var helpers = require('./helpers');
var constants = require('../constants');
var utils = require('../utils');
var BuildingLogic = require('./BuildingLogic');
var errors = require('errors');
var TaskLogic = require('./TaskLogic');
var GeneticsLogic = require('./GeneticsLogic');
var PropertyLogic = require('./PropertyLogic');
var PlanetLogic = function(data){
// toObject only exists in mongoose on the server side.
// On the client side we want to add the stuff. On the serverside
// we don't need because the data is already added on this object.
if(!data.toObject){
_.extend(this, data);
}
// We pass an object because on the server side when is saved, it should return
// this task. Would be nicer to find a better solution because this is not natural
// and not expected therefore can lead to serious bugs like not saving the task.
this.__taskLogic = new TaskLogic(this, this.task);
this.__genetics = new GeneticsLogic(this);
if(this.upgrades){
this.upgrades = this.upgrades.map(function(value){
return value ? value : 0;
})
}else{
this.upgrades = [];
}
}
PlanetLogic.create = function(data){
if(_.isString(data)){
data = PlanetLogic.decode(data);
}
return new PlanetLogic(data);
}
// Encode replace
PlanetLogic.encode = function(planetData){
var planetEncoded = [];
planetEncoded[0] = planetData._id;
planetEncoded[1] = planetData.units;
planetEncoded[2] = planetData.gold;
if(planetData.owner){
planetEncoded[3] = planetData.owner.username;
planetEncoded[4] = planetData.owner.color;
planetEncoded[5] = planetData.owner.colorB;
planetEncoded[6] = planetData.owner.colorC;
}
planetEncoded[7] = planetData.capacity;
if(planetData.task){
planetEncoded[8] = planetData.task.typeId !== void 0 ? planetData.task.typeId : void 0;
planetEncoded[9] = planetData.task.dateStarted !== void 0 ? +planetData.task.dateStarted : void 0;
planetEncoded[10] = planetData.task.payload !== void 0 ? JSON.stringify(planetData.task.payload) : void 0;
}
// We can join everything without a separator because upgrades go from 0-9 therefore
// each position is an upgrade level.
planetEncoded[11] = planetData.upgrades.join('');
planetEncoded[12] = planetData.block.id;
planetEncoded[13] = planetData.goldMined;
planetEncoded[14] = planetData.geneticCode;
planetEncoded[15] = +planetData.lastUpdated;
if(planetData._id === '1608,2095') console.log(planetEncoded)
return planetEncoded.join('|');
}
PlanetLogic.decode = function(planetEncoded){
planetEncoded = planetEncoded.split('|');
// if(planetEncoded[0] === '12789,13179') debugger
planetEncoded = planetEncoded.map(function(item){
if(item === ''){
return void 0;
}
return item;
})
var _id = planetEncoded[0];
var upgrades = planetEncoded[11];
if(upgrades){
upgrades = upgrades.split('');
}else{
upgrades = [];
}
upgrades = upgrades.map(function(u){
return Number(u);
})
// if(_id === "2850,3047") debugger
var planetData = {
_id : _id,
units : Number(planetEncoded[1]),
gold : Number(planetEncoded[2]),
owner: {
username : planetEncoded[3],
color : planetEncoded[4],
colorB : planetEncoded[5],
colorC : planetEncoded[6],
},
capacity : Number(planetEncoded[7]),
task: {
typeId : planetEncoded[8] === void 0 ? void 0 : Number(planetEncoded[8]),
dateStarted : planetEncoded[9] === void 0 ? void 0 : new Date(Number(planetEncoded[9])),
payload : planetEncoded[10] === void 0 ? void 0 : JSON.parse(planetEncoded[10]),
},
pos: {
x: Number(_id.split(',')[0]),
y: Number(_id.split(',')[1]),
},
upgrades : upgrades,
goldMined : Number(planetEncoded[13]),
block : {
id: Number(planetEncoded[12])
},
geneticCode: planetEncoded[14],
lastUpdated: planetEncoded[15] === void 0 ? void 0 : new Date(Number(planetEncoded[15])),
}
return planetData;
}
PlanetLogic.properties = {
}
_.extend(PlanetLogic.prototype, {
getUpgrade : helpers.getUpgrade,
getProperty : PropertyLogic.getProperty,
getGenetics: function(){
return this.__genetics;
},
upgradeExists: function(nameOrId){
return !!this.getUpgrade(nameOrId);
},
getPercentUnitsFilled: function(){
return this.units / this.capacity;
},
ownedByBot: function(){
return this.hasOwner() && this.owner.isBot;
},
getNearestPlanetFromArray: function(planets, minCapacity){
minCapacity = minCapacity || 0;
var closestPlanetL = Infinity, distance;
var getDistance = helpers.getDistance;
var currentPlanet = this;
var closestPlanet;
planets.forEach(function(planet){
if(!planet) return;
distance = getDistance(planet, currentPlanet)
if(distance < closestPlanetL && planet.capacity >= minCapacity){
closestPlanetL = distance;
closestPlanet = planet;
}
})
return {
planet : closestPlanet,
distance : distance
};
},
getRange: function(){
return this.getUpgrade('range', 'value');
},
increaseUnits: function(units){
this.setUnits(this.units + units)
},
removeOwner: function(){
this.setOwner(void 0);
},
isUnowned : function(){
return !(this.owner && this.owner.username !== void 0)
},
isOwned: function(){
return !this.isUnowned();
},
isOwnedBy: function(username){
if(this.isUnowned()) return false;
return this.owner.username === username;
},
getOwnerUsername: function(){
if(!this.hasOwner()) return false;
return this.owner.username;
},
// Needed because the color of the owner must be updated if no color
// has been provided such in the unowned planets. Also the color of the
// player has to be updated to the format required by the game.
setOwner: function(owner){
this.owner = owner;
if(!this.owner || this.owner.username === void 0){
this.owner = {};
this.owner.color = constants.PLANET_WITOUT_OWNER_COLOR;
this.owner.colorB = constants.PLANET_WITOUT_OWNER_COLOR_B;
this.owner.colorC = constants.PLANET_WITOUT_OWNER_COLOR_C;
}else{
this.owner.color = parseInt('0x' + owner.color);
this.owner.colorB = parseInt('0x' + owner.colorB);
this.owner.colorC = parseInt('0x' + owner.colorC);
}
},
subtractUnits: function(units){
this.setUnits(this.units - units)
},
fillWithMaxUnits: function(){
this.setUnits(this.capacity);
},
setUnits: function(units){
units = Math.round(units);
if(units > this.capacity){
units = this.capacity;
}else if(units < 0){
units = 0;
}
this.units = units;
},
getGold: function(formatted){
var value = this.gold
if(formatted){
return utils.autoformatNumber(value)
}
return value
},
getFormattedPos: function(){
return this.pos.x + ',' + this.pos.y;
},
hasOwner: function(){
return this.owner && this.owner.username !== void 0
},
getNextLevelUpgrade: function(nameOrId, dataType){
var currentLevel = this.getUpgrade(nameOrId, 'level');
var newArgs = [nameOrId, dataType, currentLevel + 1];
return this.getUpgrade.apply(this, newArgs)
},
getUnitBuildQuantity: function(formatted){
var value = this.getUpgrade('unitBuildQuantity', 'value');
if(formatted){
return utils.autoformatNumber(value) + ' ' + this.getUpgrade('unitBuildQuantity', 'unitType')
}
return value
},
getGoldMineQuantity: function(formatted){
var value = this.getUpgrade('goldMineQuantity', 'value');
if(formatted){
return utils.autoformatNumber(value) + ' ' + this.getUpgrade('goldMineQuantity', 'unitType')
}
return value
},
getRadius: function(){
return this.capacity * constants.UNITS_RADIUS_FACTOR;
},
getTask: function(){
return this.__taskLogic;
},
})
return PlanetLogic;
})
define(function(require, exports, module){
var React = require('react');
var utils = require('client/utils');
var constants = require('client/constants');
var PlanetTask = require('./generic/PlanetTask')
var tinycolor = require('tinycolor');
var moment = require('moment');
var PIXI = require('pixi');
var Phaser = require('phaser')
var ReactComponent = React.createClass({
getInitialState: function(){
return {
}
},
__onBuildUnitsClick: function(){
this.__genericTaskClick(constants.TASK_TYPES.BUILD_UNITS);
},
__onMineGoldClick: function(){
this.__genericTaskClick(constants.TASK_TYPES.GOLD_MINING);
},
__onUpgradeClick: function(){
this.__genericTaskClick(constants.TASK_TYPES.UPGRADE);
},
__onDisinfectClick: function(){
this.__genericTaskClick(constants.TASK_TYPES.DISINFECT);
},
__onRemoveOwnershipClick: function(){
this.__genericTaskClick(constants.TASK_TYPES.REMOVE_OWNERSHIP);
},
__genericTaskClick: function(taskId){
var planet = this.props.data.currentActionStore.selectedPlanet;
if(planet.getTask().taskIsDone(taskId)){
this.props.data.capi('ui.completeTask', {
planet : planet,
})
}else if(planet.getTask().taskIsRunning(taskId)){
this.props.data.capi('ui.cancelTask', {
planet : planet,
})
}else{
if(taskId === constants.TASK_TYPES.UPGRADE){
this.props.data.rightSidePanelStore.setPanel('UpgradePanel');
}else{
this.props.data.capi('ui.startTask', {
taskId : taskId,
planet : planet,
})
}
}
},
__currentPlayerIsOwner: function(){
var selectedPlanet = this.props.data.currentActionStore.selectedPlanet;
return selectedPlanet ? selectedPlanet.isOwnedBy(data.authStore.currentPlayer.username) : false;
},
componentDidMount: function(){
},
componentWillUpdate: function(){
},
__showTooltip: function(text, e){
// Set it directly on the state without setState because we don't
// need to update the UI here. The TooltipContainer will update it's UI.
this.state.tooltipId = this.props.data.tooltipStore.add({
target : e.target,
avoidOverlap : this.refs.root.getDOMNode().parentNode,
// Hackish way, is faster for now, but fix it later.
text : text,
direction : 'left'
});
},
__hideTooltip: function(){
// Set it directly on the state without setState because we don't
// need to update the UI here. The TooltipContainer will update it's UI.
this.props.data.tooltipStore.remove(this.state.tooltipId);
},
render: function(){
var planet = this.props.data.currentActionStore.selectedPlanet;
if(!planet) return null;
var ownerTooltip = 'Tells who owns this planet. ';
if(this.__currentPlayerIsOwner()){
ownerTooltip += 'You are the owner of this planet.';
}else{
if(planet.owner.username === void 0){
ownerTooltip += 'This planet is not owned by any player. Is easier to attack.'
}else{
ownerTooltip += 'You are not the owner of this planet. Another player owns this planet.'
}
}
return React.DOM.section({
className : 'planet-panel',
ref : 'root'
},
this.__currentPlayerIsOwner() ? React.DOM.section({
className: 'task-section '
},
React.DOM.h2({}, 'planet tasks'),
React.DOM.ul({
className: 'task-list'
},
new PlanetTask({
key : 'mineGold',
taskId : constants.TASK_TYPES.GOLD_MINING,
planet : planet,
currentPlayerIsOwner : this.__currentPlayerIsOwner(),
onClick : this.__onMineGoldClick,
tooltipStore : this.props.data.tooltipStore,
}),
new PlanetTask({
key : 'buildUnits',
taskId : constants.TASK_TYPES.BUILD_UNITS,
planet : planet,
currentPlayerIsOwner : this.__currentPlayerIsOwner(),
onClick : this.__onBuildUnitsClick,
tooltipStore : this.props.data.tooltipStore,
}),
new PlanetTask({
key : 'disinfect',
taskId : constants.TASK_TYPES.DISINFECT,
planet : planet,
currentPlayerIsOwner : this.__currentPlayerIsOwner(),
onClick : this.__onDisinfectClick,
tooltipStore : this.props.data.tooltipStore,
}),
new PlanetTask({
key : 'upgrade',
taskId : constants.TASK_TYPES.UPGRADE,
planet : planet,
currentPlayerIsOwner : this.__currentPlayerIsOwner(),
onClick : this.__onUpgradeClick,
tooltipStore : this.props.data.tooltipStore,
}),
new PlanetTask({
key : 'remove ownership',
taskId : constants.TASK_TYPES.REMOVE_OWNERSHIP,
planet : planet,
currentPlayerIsOwner : this.__currentPlayerIsOwner(),
onClick : this.__onRemoveOwnershipClick,
tooltipStore : this.props.data.tooltipStore,
})
)
) : null,
React.DOM.section({
className: 'info'
},
React.DOM.h2({}, 'planet info'),
// Not used now, but might come back.
// React.DOM.div({
// className: 'planet-preview small',
// style: {
// backgroundColor : '#'+utils.decimalToHex(planet.owner.color),
// borderColor : '#'+utils.decimalToHex(planet.owner.colorB),
// }
// }),
React.DOM.ul({
className: 'info-items'
},
// Not used now, but might come back.
// React.DOM.li({},
// React.DOM.span({className: 'value'}, planet.getFormattedPos()),
// React.DOM.span({className: 'key'}, 'pos x, y')
// ),
React.DOM.li({
onMouseEnter : this.__showTooltip.bind(this, ownerTooltip),
onMouseLeave : this.__hideTooltip,
},
React.DOM.span({className: 'value'}, (planet.owner.username || '?') + (this.__currentPlayerIsOwner() ? ' (you)' : '')),
React.DOM.span({className: 'key'}, 'owner')
),
React.DOM.li({
onMouseEnter : this.__showTooltip.bind(this, 'Shows how many units you have on the planet and the maximum capacity. 30/60 means that you have 30 units and the maximum number of units that you can have on this planet is 60.'),
onMouseLeave : this.__hideTooltip,
},
React.DOM.span({className: 'value'}, utils.autoformatNumber(planet.units) + '/' + planet.capacity + ' u'),
React.DOM.span({className: 'key'}, 'units/capacity')
),
React.DOM.li({
onMouseEnter : this.__showTooltip.bind(this, 'Gold remaining for mining on this planet. Gold is not infinite therefore every time you gold mine you get the gold from the ground. Some day it will end.'),
onMouseLeave : this.__hideTooltip,
},
React.DOM.span({className: 'value'}, planet.getGold(true) + ' g'),
React.DOM.span({className: 'key'}, 'gold on planet')
),
React.DOM.li({
onMouseEnter : this.__showTooltip.bind(this, 'The more zombie you have the less efficient is your planet (If you have less than 10% then no problem). Lowers the ship attack and speed and planet defense. Be careful that if you send a ship from a planet with zombies you might infect another planet. In order to disinfect use the disinfect task on the planet.'),
onMouseLeave : this.__hideTooltip,
},
React.DOM.span({className: 'value'}, utils.autoformatNumber(planet.getGenetics().getGene('zombie', 'percent') * 100) + ' %z'),
React.DOM.span({className: 'key'}, 'zombie')
),
React.DOM.li({
onMouseEnter : this.__showTooltip.bind(this, 'The attack of your planet. The more you have, the more units you will kill when you attack another planet.'),
onMouseLeave : this.__hideTooltip,
},
React.DOM.span({className: 'value'}, utils.autoformatNumber(planet.getProperty('shipAttack')) + ' a'),
React.DOM.span({className: 'key'}, 'ship attack')
),
React.DOM.li({
onMouseEnter : this.__showTooltip.bind(this, 'The defense of your planet. The more you have the harder is to for other players to kill your units on this planet.'),
onMouseLeave : this.__hideTooltip,
},
React.DOM.span({className: 'value'}, utils.autoformatNumber(planet.getProperty('planetDefense')) + ' d'),
React.DOM.span({className: 'key'}, 'planet defense')
)
// Not used now, but might come back.
// React.DOM.li({},
// React.DOM.span({className: 'value'}, utils.autoformatNumber(planet.getProperty('shipSpeed')) + ' px/m'),
// React.DOM.span({className: 'key'}, 'ship speed')
// ),
// React.DOM.li({},
// React.DOM.span({className: 'value'}, moment(planet.lastUpdated).startOf('seconds').fromNow()),
// React.DOM.span({className: 'key'}, 'last updated')
// ),
// React.DOM.li({},
// React.DOM.span({className: 'value'}, planet.getTask().getLastType() !== void 0 ? constants.getTaskBy('id', planet.getTask().getLastType()).text : 'no tasks done yet'),
// React.DOM.span({className: 'key'}, 'last task')
// )
)
// Not used now, but might come back.
// ,
// React.DOM.ul({
// className : 'genetic-code',
// title : 'Planet DNA/Genetic code.'
// },
// planet.geneticCode.split('').map(function(geneticCodeChar, i){
// var bgColor = tinycolor('#'+planet.getGenetics().getColorAtIndex(i)).setAlpha(0.5);
// var textColor = tinycolor.mostReadable(bgColor, ["#FFF", "#000"]).toHexString();
// return React.DOM.li({
// style: {
// color : textColor,
// backgroundColor : tinycolor('#'+planet.getGenetics().getColorAtIndex(i)).setAlpha(1),
// }
// }, geneticCodeChar)
// })
// )
)
)
}
});
return ReactComponent;
});
define(function(require, exports, module){
var React = require('react');
var utils = require('client/utils');
var constants = require('client/constants');
var ReactComponent = React.createClass({
getInitialState: function(){
return {}
},
componentDidMount: function(){
this.__updateTimeMissing();
this.__interval = setInterval(this.__updateTimeMissing.bind(this), 1000/60);
},
componentWillReceiveProps: function(newProps){
this.__updateTimeMissing(newProps);
},
componentWillUnmount: function(){
clearInterval(this.__interval);
},
__updateTimeMissing: function(props){
props = props || this.props;
var remainingTime = void 0;
if(this.__currentTaskIsActive()){
remainingTime = props.planet.getTask().taskEndsInMs();
}
this.setState({
remainingTime : remainingTime,
percentDone : props.planet.getTask().getCurrentTaskPercentDone()
})
},
__currentTaskIsActive: function(){
return this.props.planet.getTask().getCurrentTaskId() === this.props.taskId;
},
__getTaskTime: function(){
var taskId = this.props.taskId;
var planet = this.props.planet;
if(taskId === constants.TASK_TYPES.GOLD_MINING){
return planet.getTask().getMineGoldTaskTime();
}
else if(taskId === constants.TASK_TYPES.BUILD_UNITS){
return planet.getTask().getBuildUnitsTaskTime();
}
else if(taskId === constants.TASK_TYPES.UPGRADE){
return planet.getTask().getUpgradeTaskTime();
}
else if(taskId === constants.TASK_TYPES.DISINFECT){
return planet.getTask().getTaskTime(constants.TASK_TYPES.DISINFECT);
}
},
__getTaskTitle: function(){
var taskId = this.props.taskId;
return constants.getTaskBy('id', taskId).text;
},
__getTaskState: function(){
var planet = this.props.planet;
// The state of the task on the planet is one thing but the
// state of this task is another even if they are similar.
// This is relative to the current view task.
if(this.__currentTaskIsActive()){
return planet.getTask().getCurrentTaskState();
}else if(planet.getTask().isActive()){
return 'disabled';
}else{
return 'idle';
}
},
__getTaskData: function(){
var taskId = this.props.taskId;
var planet = this.props.planet;
var taskData = {
title: this.__getTaskTitle()
};
if(taskId === constants.TASK_TYPES.GOLD_MINING){
taskData.value = '+' + utils.autoformatNumber(planet.getProperty('goldMineQuantity')) + ' g'
taskData.needs = [
{
title : 'time',
value : planet.getTask().getMineGoldTaskTime(true)
}
]
}
else if(taskId === constants.TASK_TYPES.BUILD_UNITS){
taskData.value = '+' + utils.autoformatNumber(planet.getProperty('unitBuildQuantity')) + ' u'
taskData.needs = [
{
title : 'time',
value : planet.getTask().getBuildUnitsTaskTime(true)
},
{
title : 'gold',
value : utils.autoformatNumber(planet.getTask().getTaskGold(constants.TASK_TYPES.BUILD_UNITS))
}
]
}
else if(taskId === constants.TASK_TYPES.UPGRADE){
}
else if(taskId === constants.TASK_TYPES.DISINFECT){
taskData.value = '-' + utils.autoformatNumber(25) + ' %'
taskData.needs = [
{
title : 'time',
value : utils.autoformatNumber(planet.getTask().getTaskTime(constants.TASK_TYPES.DISINFECT, void 0, true)/1000) + ' s'
},
{
title : 'gold',
value : utils.autoformatNumber(planet.getTask().getTaskGold(constants.TASK_TYPES.DISINFECT))
}
]
}
else if(taskId === constants.TASK_TYPES.REMOVE_OWNERSHIP){
taskData.needs = [
{
title : 'time',
value : utils.autoformatNumber(planet.getTask().getTaskTime(constants.TASK_TYPES.REMOVE_OWNERSHIP, void 0, true)/1000) + ' s'
},
{
title : 'gold',
value : utils.autoformatNumber(planet.getTask().getTaskGold(constants.TASK_TYPES.REMOVE_OWNERSHIP))
}
]
}
taskData.state = this.__getTaskState();
taskData.stateDetails = this.__getClickInfo();
return taskData;
},
__getClickInfo: function(){
var state = this.__getTaskState();
var taskId = this.props.taskId;
if(state === 'running'){
return this.props.currentPlayerIsOwner ? 'cancel this task' : '';
}else if(state === 'done'){
var currentTask = constants.getTaskBy('id', this.props.taskId);
return this.props.currentPlayerIsOwner ? currentTask.onDone : '';
}else if(state === 'idle'){
if(taskId === constants.TASK_TYPES.UPGRADE){
return this.props.currentPlayerIsOwner ? 'view upgrades' : '';
}else{
return this.props.currentPlayerIsOwner ? 'start' : '';
}
}else if(state === 'disabled'){
return this.props.currentPlayerIsOwner ? 'wait for other tasks to end' : 'you don\'t own this planet';
}
},
getStateSection: function(taskData){
var remainingTimeString = '';
if(this.state.remainingTime){
remainingTimeString = utils.autoformatNumber(this.state.remainingTime/1000) +
's remaining (' + Math.round(this.state.percentDone) + '%)';
}
return React.DOM.span({
className: 'state ' + taskData.state},
'state: ' + taskData.state + ' ' + Math.round(this.state.percentDone) + '%')
},
getProgressBarSection: function(){
if(!this.__currentTaskIsActive()) return null;
return React.DOM.span({className: 'percent-done',
style: {
width: this.state.percentDone + '%'
}
})
},
getRequirementsSection: function(taskData){
if(!taskData.needs) return null;
var needs = taskData.needs.map(function(need){
return React.DOM.li({
className: need.title
}, need.title + ' ' + need.value);
})
return React.DOM.ul({className: 'more'}, needs);
},
__showTooltip: function(e){
// Set it directly on the state without setState because we don't
// need to update the UI here. The TooltipContainer will update it's UI.
this.state.tooltipId = this.props.tooltipStore.add({
target : this.refs.root.getDOMNode(),
avoidOverlap : this.refs.root.getDOMNode().parentNode.parentNode,
text : this.getTooltipContents(),
direction : 'left'
});
},
__hideTooltip: function(){
// Set it directly on the state without setState because we don't
// need to update the UI here. The TooltipContainer will update it's UI.
this.props.tooltipStore.remove(this.state.tooltipId);
},
getTooltipContents: function(){
var taskData = this.__getTaskData();
var planet = this.props.planet;
var currentTask = constants.getTaskBy('id', this.props.taskId);
var children = [];
children.push(taskData.title + ': ' + currentTask.details);
if(taskData.value !== void 0){
children.push(React.DOM.li({}, 'You will get: '));
children.push(React.DOM.li({}, taskData.value))
}
if(this.__currentTaskIsActive()){
children.push(React.DOM.li({}, this.getStateSection(taskData)))
children.push(React.DOM.li({}, taskData.stateDetails))
}else{
children.push(React.DOM.li({}, 'You need: '));
children.push(React.DOM.li({}, this.getRequirementsSection(taskData)));
}
if(this.__currentTaskIsActive()){
children.push(React.DOM.li({}, this.getProgressBarSection()));
}
return React.DOM.ul({}, children);
},
render: function(){
var title;
var state = this.__getTaskState();
if(state === 'done'){
title = this.__getClickInfo();
}else{
title = this.__getTaskTitle();
}
return React.DOM.li({
className : 'item ' + state,
ref : 'root'
},
React.DOM.div({
onClick : this.props.onClick,
className : 'main-action'
},
this.getProgressBarSection(),
React.DOM.h3({className: 'title'}, title)
),
React.DOM.div({
className : 'help',
onMouseEnter : this.__showTooltip,
onMouseLeave : this.__hideTooltip,
}, '?')
);
}
});
return ReactComponent;
});
define(function(require, exports, module){
var _ = require('underscore');
var constants = require('constants');
var utils = require('utils');
// This is a 2 way fn because we need to know the level
// based on xp and reverse (xp required per level).
// We use the `Math.floor` because will not create errors
// when the xp approach the limit of the levels.
var levelToXp = function(level){
return Math.floor(Math.pow(level, 3))
}
var xpToLevel = function(xp){
return Math.floor(Math.pow(xp, 1/3));
}
var levelToPlanets = function(level){
return constants.PLANETS_GIVEN_TO_NEW_PLAYERS + Math.floor(Math.pow(level, 1/0.75))
}
var planetsToLevel = function(planets){
return Math.floor(Math.pow(planets, 0.75)) - constants.PLANETS_GIVEN_TO_NEW_PLAYERS;
}
// Test the player level fn.
// var i = 1000;
// while(i){
// i -= 1;
// var level = xpToLevel(i);
// var xp = levelToXp(level);
// console.log(i, xp, level)
// }
var PlayerLogic = function(playerData){
_.extend(this, playerData);
// This is necessary because the player on the planet's or ships owner are not full players.
this.fullPlayer = true;
}
PlayerLogic.STATES = {
OFFLINE : 0,
ONLINE : 1,
NOT_FOCUSSED : 2
}
_.extend(PlayerLogic.prototype, {
usernameIsMissing: function(){
// This is not 100% bulletproof but will work 99.9999%
// because no user would have his username the exact same as
// his internal id.
return this._id === this.username;
},
getLevel: function(){
return xpToLevel(this.XP);
},
// This will get the seconds played at any time.
computeSecondsPlayed: function(){
var currentIntervalSeconds = Math.round((+new Date - this.stats.lastTimeOnline)/1000);
return this.stats.secondsPlayed + currentIntervalSeconds;
},
// Will return the number of XP needed for this level.
getThisLevelXP: function(){
var level = this.getLevel();
return levelToXp(level);
},
getNextLevelXP: function(){
var level = this.getLevel();
return levelToXp(level + 1);
},
getMaxPlanets: function(){
return levelToPlanets(this.getLevel());
},
getNumOfPlanetsOwned: function(){
return this.stats.planetsOwnedN;
},
increaseNumOfPlanetsOwned: function(value){
if(!value) return;
this.stats.planetsOwnedN += value;
if(this.stats.planetsOwnedN < 0) this.stats.planetsOwnedN = 0;
if(this.stats.planetsOwnedN > this.getMaxPlanets()) throw new Error('You should not increase the # of planets because the limits are off.')
},
canHaveMorePlanets: function(howMany){
howMany = howMany === void 0 ? 1 : howMany;
return this.stats.planetsOwnedN + howMany <= this.getMaxPlanets()
},
getTotalGoldMined: function(){
return this.stats.totalGoldMined;
},
setState: function(newState){
var stateChanged = this.state !== newState;
prevState = this.state;
this.state = newState;
var STATES = PlayerLogic.STATES
if(newState === STATES.ONLINE){
this.stats.lastTimeOnline = new Date();
}
this.lastPing = new Date();
if([STATES.OFFLINE, STATES.NOT_FOCUSSED].indexOf(newState) !== -1 && prevState === STATES.ONLINE){
this.stats.secondsPlayed = this.computeSecondsPlayed();
}
}
})
return PlayerLogic;
});
define(function(require, exports, module){
var _ = require('underscore');
var utils = require('utils');
var constants = require('client/constants');
var PlanetUpgrades = require('client/upgrades/PlanetUpgrades');
var helpers = {
getNearestPlanet: function(planet, availablePlanets, notAvailablePlanets){
var closest = Infinity;
var choosenPlanetB;
var i = availablePlanets.length;
while(i--){
var planetB = availablePlanets[i];
if(notAvailablePlanets.indexOf(planetB) !== -1) continue;
var dist = utils.distanceBetween2Points(planet.pos, planetB.pos);
if(dist < closest){
closest = dist;
choosenPlanetB = planetB;
}
}
return choosenPlanetB;
},
getNearestPlanets: function(availablePlanets, howMany){
var nearestPlanets = [];
var i = howMany;
var planet;
while(i--){
planet = helpers.getNearestPlanet(availablePlanets[0], availablePlanets, nearestPlanets);
if(planet){
nearestPlanets.push(planet)
}
}
return nearestPlanets;
},
// You give this fn the planets you want to be reachable (targetPlanets)
// and the planets you have available (availablePlanets). Then it returns
// the first reachable planet. Then you specify the limit which means
// that the algorithm will stop after finding X planets in range.
findInRangePlanets: function(targetPlanets, availablePlanets, limit){
var limit = limit || 1;
var l = availablePlanets.length;
var availablePlanet;
var nearestPlanet;
var minCapacity = 50;
var targetPlanet;
var k = targetPlanets.length;
var results = [];
for(var i = 0; i < l; i++){
if(results.length >= limit) break
availablePlanet = availablePlanets[i];
var range = availablePlanet.getRange();
for(var j = 0; j < k; j++){
targetPlanet = targetPlanets[j];
var distance = helpers.getDistance(targetPlanet, availablePlanet);
if(distance < range){
// You don't need to check if this already exists in the results
// because the loop don't run twice for the same combination.
results.push({
to : targetPlanet,
from : availablePlanet,
distance : distance
})
}
}
}
function shuffle(o){ //v1.0
for(var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
return o;
};
return shuffle(results);
},
// name: of the upgrade
// dataType: [time, gold, value, level, unitType]
// atLevel: undefined will return the values at the planet level, if specified will return at that level.
getUpgrade : function(nameOrId, dataType, atLevel){
var upgrade;
if(_.isNumber(nameOrId)){
upgrade = PlanetUpgrades[nameOrId];
}else{
upgrade = PlanetUpgrades.getByName(nameOrId);
}
if(!upgrade){
return false; console.error(new Error('No upgrade nameOrId: "' + nameOrId + '" found.'));
}
var upgradeId = upgrade.id;
if(atLevel === void 0){
atLevel = this.upgrades[upgradeId] || 0
}
if(dataType === 'level'){
return atLevel;
}else if(!dataType){
return upgrade;
}else{
var map = {
time : 'getTime',
gold : 'getPrice',
price : 'getPrice',
value : 'getValue',
level : null,
unitType : 'unitType'
}
var upgradeMethod = map[dataType];
var fn = upgrade[upgradeMethod];
if(_.isFunction(fn)){
return fn(atLevel)
}else{
var value = fn;
return value;
}
}
},
getDistance : function(fromPlanet, toPlanet){
var distance = utils.distanceBetween2Points({x: fromPlanet.pos.x, y: fromPlanet.pos.y},
{x: toPlanet.pos.x , y: toPlanet.pos.y });
var radiusFix = fromPlanet.getRadius() + toPlanet.getRadius();
return distance - radiusFix;
},
planetHasSameOwnerAsShip: function(planet, ship){
return planet.owner && planet.owner.username === ship.owner.username
},
// Options:
// check the send ship
//
// Returns:
// err : a string if can't send ship
// data : true if can send ship
canSendShip: function(options){
var fromPlanet = options.fromPlanet;
var toPlanet = options.toPlanet;
var player = options.player;
if(options.units < 0) return {err: 'negative units'};
if(!fromPlanet.owner) return {err: 'no owner on from planet'};
if(!player) return {err: 'no player provided'};
if(!options.isBot && fromPlanet.owner.username !== player.username){
return {err: 'current player is not the owner of the from planet'};
}
if(fromPlanet._id === toPlanet._id) return {err: 'same planet'};
if(fromPlanet.units === 0) return {err: 'units === 0'};
if(fromPlanet.units < options.units) return {err: 'units < than requested'};
var distance = helpers.getDistance(fromPlanet, toPlanet);
var maxDistance = fromPlanet.getRange();
if(distance > maxDistance) return {err: 'distance: '+ distance + ' > ' + maxDistance};
if(player.isGhost){
if(toPlanet.hasOwner() && toPlanet.owner.username !== player.username){
return {err: 'isGhost'};
}
}
return {data: true};
},
// Options:
// check the send ship
//
// Returns:
// shipData
createShip: function(options){
var fromPlanet = options.fromPlanet;
var toPlanet = options.toPlanet;
var player = options.player;
var shipData = {
owner: {
username : player.username,
color : player.color,
colorB : player.colorB,
colorC : player.colorC,
isBot : player.bot.isBot
},
from: {
x : fromPlanet.pos.x,
y : fromPlanet.pos.y,
block : fromPlanet.block.id
},
to: {
x : toPlanet.pos.x,
y : toPlanet.pos.y,
block : toPlanet.block.id
},
// Enforce positive and absolute numbers.
units : Math.round(Math.abs(options.units)),
// Ensure is a copy so no unexpected changes will be reflected in the planet.
upgrades : fromPlanet.upgrades.slice(0),
geneticCode : fromPlanet.geneticCode.slice(0),
}
if(options.isBot) shipData.owner.isBot = true;
return shipData;
},
// This will actually change the inputs you provided.
//
// Options:
// fromPlanet
// toPlanet
// player
// isBot
// units
//
// Returns:
// err
// data
// ship
// fromPlanet
// apiArgs: this can used to send the request without anymore data processing
sendShip: function(options){
var fromPlanet = options.fromPlanet;
var toPlanet = options.toPlanet;
var canSendShip = helpers.canSendShip(options)
if(canSendShip.err) return canSendShip;
if(!canSendShip.data) return {err: 'unknown error'};
var ship = helpers.createShip(options);
if(fromPlanet.units < ship.units) return {err: 'planet.units is negative'};
fromPlanet.subtractUnits(ship.units);
return {
data: ship,
apiArgs: {
fromPlanetId : fromPlanet._id,
toPlanetId : toPlanet._id,
units : ship.units
}
};
},
onShipDestinationReached: function(ship, planet, shipOwner, planetOwner){
if(!shipOwner) return {err: 'no ship owner'}
var friendlyPlanet = planet.owner && ship.owner && ship.owner.username === planet.owner.username;
if(planet && !planetOwner){
// On the client side we don't need the planetOwner but on the server side
// we need it in order to update XP and planets owned. We could load the
// planetPlayer on the client side if needed later, but for now will make
// stuff more complex and requires another request.
}
var warn;
var changeOwner = false;
if(friendlyPlanet){
planet.increaseUnits(ship.units);
}else{
var shipAttack = ship.getProperty('shipAttack');
var planetDefense = planet.getProperty('planetDefense');
// So it's a lot easier to conquer an unOwned planet
var temp = planetDefense + shipAttack
var virtualUnits = planet.units * planetDefense / temp - ship.units * shipAttack / temp;
var newUnits = Math.round(virtualUnits);
if(virtualUnits < 0){
newUnits = Math.min(-virtualUnits, ship.units);
changeOwner = true;
}
if(newUnits < 0){
newUnits = 0;
}
var destroyedUnits = 0;
if(changeOwner){
destroyedUnits = planet.units;
}else{
destroyedUnits = planet.units - newUnits;
}
var multiplier = planet.isUnowned() ? constants.XP.UNOWNED_MULTIPLIER : 1;
// On the client side you don't care about increasing enemies XP
if(!shipOwner.XP) shipOwner.XP = 0;
shipOwner.XP += destroyedUnits * constants.XP.PER_UNIT_DESTROYED * planetDefense * multiplier;
// This will lead to client side inconsistencies because this will never happen when the ship
// owner is not the current player. This is fixed when the block is updated.
if(changeOwner && shipOwner.fullPlayer){
if(!shipOwner.canHaveMorePlanets(1)){
changeOwner = false;
// We set units to 0 because the ship has destroyed all the units but can't own
// this new planet therefore the planet will remain empty.
newUnits = 0;
warn = "You can't own more planets. You need a higher level."
}
}
planet.setUnits(newUnits);
if(changeOwner){
// Ship owner always exists. But:
// - On the client will only be full player if is the current player;
// - On the server will always be full player.
if(shipOwner.fullPlayer){
shipOwner.increaseNumOfPlanetsOwned(1);
shipOwner.XP += constants.XP.PER_PLANET_OWNED * planetDefense * multiplier;
}
// We will have planet owner:
// - On the client only when is the planet owner is the current player;
// - On the server side all the time;
if(planetOwner && planetOwner.fullPlayer){
planetOwner.increaseNumOfPlanetsOwned(-1)
}
// On the client side the shipOwner doesn't have the ownsBlocksIds property because
// is only the cached object located as ship.owner.
if(shipOwner.ownsBlocksIds && shipOwner.ownsBlocksIds.indexOf(planet.block.id) === -1){
shipOwner.ownsBlocksIds.push(planet.block.id);
}
}
shipOwner.XP = Math.round(shipOwner.XP*100)/100;
}
planet.getGenetics().mutateByShip(ship);
var planetUnits = planet.units;
if(changeOwner){
// This only sets the owner data stored in the ship to the new
// planet. The ship.owner contains less data than shipOwner.
planet.owner = ship.owner;
}else if(planetUnits === 0){
// Lucky owner, this planet is still his.
}
ship.destinationReached = true;
return {
data: {
friendly : friendlyPlanet,
destroyedUnits : destroyedUnits,
ownerChanged : changeOwner,
warn : warn
}
};
},
}
return helpers;
})
define(function(require, exports, module){
var utils = require('client/utils');
var constants = require('client/constants');
var React = require('react');
var ReactComponent = React.createClass({
getInitialState: function(){
return {
tooltipBB: {
top : 0,
right : 0,
bottom : 0,
left : 0,
width : 0,
height : 0
},
blink: true
}
},
componentDidMount: function(){
// We need to make another update after the tooltip is first rendered
// to know it's width and height. This is needed in order to make the
// tooltip to center itself around some point.
// Commented at this moment because the tooltip will need to update
// on each frame because when scrolling will not keep the correct
// position relative to the target object.
// this.setState({
// tooltipBB : this.refs.root.getDOMNode().getBoundingClientRect()
// });
},
render: function(){
var targetBB = this.props.tooltip.target.getBoundingClientRect();
var avoidOverlapBB = this.props.tooltip.avoidOverlap.getBoundingClientRect();
var tooltipBB;
if(this.refs.root){
tooltipBB = this.refs.root.getDOMNode().getBoundingClientRect();
}else{
tooltipBB = {
top : 0,
right : 0,
bottom : 0,
left : 0,
width : 0,
height : 0
}
}
var viewportSize = {
height: window.innerHeight
}
var direction = this.props.tooltip.direction;
var offsetX = 10;
var lineHeight = 2;
var tooltipStyle = {
top : targetBB.top + targetBB.height / 2 - tooltipBB.height / 2,
};
// Hide the tooltip on the first frame, as it will not be
// in the correct position and will create some bad animations.
if(tooltipBB.width === 0){
tooltipStyle.opacity = 0;
}
// Keep the tooltip in the view (top and bottom correction).
if(tooltipStyle.top + tooltipBB.height > viewportSize.height){
tooltipStyle.top -= tooltipStyle.top + tooltipBB.height - viewportSize.height
}else if(tooltipStyle.top < 0){
tooltipStyle.top = 0;
}
var lineStyle = {
top : targetBB.top + targetBB.height / 2 - 1
}
// Makes the line to don't be hidden and to have an offset from the edges.
var lineOffsetEdges = 5;
if(lineStyle.top + lineOffsetEdges + lineHeight > viewportSize.height){
lineStyle.top -= lineStyle.top + lineOffsetEdges + lineHeight - viewportSize.height
}
if(lineStyle.top < 0){
lineStyle.top = lineOffsetEdges;
}
if(direction === 'right'){
tooltipStyle.left = avoidOverlapBB.left + avoidOverlapBB.width + offsetX;
lineStyle.left = targetBB.left + targetBB.width;
lineStyle.width = avoidOverlapBB.left + avoidOverlapBB.width + offsetX - (targetBB.left + targetBB.width)
}else if(direction === 'left'){
tooltipStyle.left = avoidOverlapBB.left - tooltipBB.width - offsetX;
lineStyle.left = tooltipStyle.left + tooltipBB.width;
lineStyle.width = targetBB.left - lineStyle.left;
lineStyle.width = lineStyle.width < 0 ? -lineStyle.width : lineStyle.width;
}
return React.DOM.div({className: 'tooltip-wrapper'},
React.DOM.div({
className : 'tooltip' + (this.state.blink ? ' blink' : ''),
ref : 'root',
style : tooltipStyle
}, this.props.tooltip.text),
React.DOM.div({
className: 'line',
style: lineStyle
})
)
}
});
return ReactComponent;
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment