Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- var PlayerTracker = require('../PlayerTracker');
- var gameServer = require('../GameServer');
- function BotPlayer() {
- PlayerTracker.apply(this, Array.prototype.slice.call(arguments));
- //this.color = gameServer.getRandomColor();
- // AI only
- this.gameState = 0;
- this.predators = []; // List of cells that can eat this bot
- this.threats = []; // List of cells that can eat this bot but are too far away
- this.splitThreats = []; // Sublist of threats but split threats are up to 3.5x size of cell
- this.prey = []; // List of cells that can be eaten by this bot
- this.food = [];
- this.ejectedMass = [];
- this.virus = []; // List of viruses
- this.obstacles = []; // Viruses or same-team cells that will be avoided
- this.splitCooldown = 0; // When I split, this will be 16
- this.juke = false;
- this.target;
- this.targetVirus; // Virus used to shoot into the target
- this.virusShots = 0; // Amount of pressed W to explode target via target virus
- this.ejectMass = 0; // Amount of times to eject mass
- this.mouse = {
- x: 0,
- y: 0
- };
- this.targetPos = {
- x: 0,
- y: 0
- };
- }
- module.exports = BotPlayer;
- BotPlayer.prototype = new PlayerTracker();
- // Functions
- BotPlayer.prototype.getLowestCell = function() {
- // Gets the cell with the lowest mass
- if (this.cells.length <= 0) {
- return null; // Error!
- }
- // Starting cell
- var lowest = this.cells[0];
- for (i = 1; i < this.cells.length; i++) {
- if (lowest.mass > this.cells[i].mass) {
- lowest = this.cells[i];
- }
- }
- return lowest;
- };
- BotPlayer.prototype.getHighestCell = function() {
- // Gets the cell with the highest mass
- if (this.cells.length <= 0) {
- return null; // Error!
- }
- // Starting cell
- var highest = this.cells[0];
- for (i = 1; i < this.cells.length; i++) {
- if (highest.mass > this.cells[i].mass) {
- highest = this.cells[i];
- }
- }
- return highest;
- };
- // Don't override, testing to use more accurate way.
- /*
- BotPlayer.prototype.updateSightRange = function() { // For view distance
- var range = 1000; // Base sight range
- if (this.cells[0]) {
- range += this.cells[0].getSize() * 2.5;
- }
- this.sightRangeX = range;
- this.sightRangeY = range;
- }; */
- BotPlayer.prototype.update = function() { // Overrides the update function from player tracker
- // Remove nodes from visible nodes if possible
- for (var i = 0; i < this.nodeDestroyQueue.length; i++) {
- var index = this.visibleNodes.indexOf(this.nodeDestroyQueue[i]);
- if (index > -1) {
- this.visibleNodes.splice(index, 1);
- }
- }
- // Respawn if bot is dead
- if (this.cells.length <= 0) {
- this.gameServer.gameMode.onPlayerSpawn(this.gameServer, this);
- if (this.cells.length == 0) {
- // If the bot cannot spawn any cells, then disconnect it
- this.socket.close();
- return;
- }
- }
- if (this.splitCooldown > 0) this.splitCooldown -= 1;
- this.shouldUpdateNodes();
- if (Math.random() > 0.5) return;
- setTimeout(function() {
- // Calculate what should I do
- var cell = this.getLowestCell();
- this.clearLists();
- for (var i in this.visibleNodes) {
- var check = this.visibleNodes[i];
- this.addCellToList(cell, check);
- }
- // Get gamestate
- var newState = this.getState(cell);
- if ((newState != this.gameState) && (newState != 4)) {
- // Clear target
- this.target = null;
- }
- this.gameState = newState;
- // Action
- this.decide(cell);
- }.bind(this), 0);
- // Reset queues
- this.nodeDestroyQueue = [];
- this.nodeAdditionQueue = [];
- };
- BotPlayer.prototype.shouldUpdateNodes = function() {
- if ((this.tickViewBox <= 0) && (this.gameServer.run)) {
- this.visibleNodes = this.calcViewBox();
- this.tickViewBox = 2;
- } else {
- this.tickViewBox--;
- }
- };
- BotPlayer.prototype.getState = function(cell) {
- if (this.gameState == 4) {
- // Continue to shoot viruses
- return 4;
- }
- // Check for predators
- if (this.predators.length <= 0) {
- if (this.prey.length > 0) {
- return 3;
- }
- else if (this.food.length > 0) {
- return 1;
- }
- } else {
- if (this.cells.length == 1 && cell.mass > 180) {
- var t = this.getBiggest(this.threats);
- var tl = this.findNearbyVirus(t, 500, this.virus);
- if (tl != false) {
- this.target = t;
- this.targetVirus = tl;
- return 4;
- } else return 2; // Run
- } else {
- // Run
- return 2;
- }
- }
- return 0; // None
- };
- BotPlayer.prototype.decide = function(cell) {
- switch (this.gameState) {
- case 1: // Target food
- var target = this.food[0];
- var angle = this.angle(target.position, cell.position);
- var dist = this.distance(target.position, cell.position);
- angle = this.avoidObstacles(cell, angle); // Avoid obstacles in the way
- var x1 = cell.position.x + (dist * Math.sin(angle));
- var y1 = cell.position.y + (dist * Math.cos(angle));
- this.updateMouse(x1, y1);
- break;
- case 2: // Run from predators
- var enemyCenter = this.combineVectors(this.predators);
- // Get angle from enemy center to my cell
- var angle = this.angle(cell.position, enemyCenter);
- // Direction to move
- var x1 = cell.position.x + (700 * Math.sin(angle));
- var y1 = cell.position.y + (700 * Math.cos(angle));
- this.updateMouse(x1, y1);
- if (this.juke) {
- // Juking
- this.gameServer.splitCells(this);
- }
- break;
- case 3: // Target prey
- var target = this.bestPrey(cell, this.prey);
- var angle = this.angle(target.position, cell.position);
- var dist = this.distance(target.position, cell.position);
- angle = this.avoidObstacles(cell, angle); // Avoid obstacles in the way
- var x1 = cell.position.x + (dist * Math.sin(angle));
- var y1 = cell.position.y + (dist * Math.cos(angle));
- this.updateMouse(x1, y1);
- // Look if I can splitkill it
- if (this.splitCooldown <= 0 && this.canSplitkill(cell, target) && this.cells.length < 3) {
- // I can do it
- // See if I will have problems if I split
- if (this.splitThreats.length > 0) {
- if (this.getBiggest(this.splitThreats).mass > this.mass * 1.3) {
- break; // I will
- }
- }
- // Update mouse to directly follow the prey
- this.updateMouse(target.position.x, target.position.y);
- // Split!
- this.gameServer.splitCells(this);
- this.splitCooldown = 16;
- }
- break;
- case 4: // Shoot virus
- if ((!this.target) || (!this.targetVirus) || (!this.cells.length == 1) ||
- (this.visibleNodes.indexOf(this.target) == -1) || (this.visibleNodes.indexOf(this.targetVirus) == -1)) {
- this.gameState = 0; // Reset
- this.target = null;
- break;
- }
- // Make sure target is within range
- var dist = this.getDist(this.targetVirus, this.target) - (this.target.getSize() + 100);
- if (dist > 500) {
- this.gameState = 0; // Reset
- this.target = null;
- break;
- }
- // Find angle of vector between target and virus
- var angle = this.getAngle(this.targetVirus, this.target);
- // Get my angle to target virus
- var ourAngle = this.getAngle(cell, this.targetVirus);
- // Check if I'm in position
- if ((ourAngle <= (angle + .25)) && (ourAngle >= (angle - .25))) {
- // In position!
- this.updateMouse(this.targetVirus.position.x, this.targetVirus.position.y);
- // Shoot W
- if (this.virusShots <= this.gameServer.config.virusFeedAmount) {
- this.gameServer.ejectMass(this);
- this.virusShots++;
- break;
- }
- // Cleanup
- this.gameState = 0; // Reset
- this.virusShots = 0;
- this.target = null;
- } else {
- // Move to position
- var r = cell.getSize();
- var x1 = this.targetVirus.position.x + ((350 + r) * Math.sin(angle));
- var y1 = this.targetVirus.position.y + ((350 + r) * Math.cos(angle));
- this.updateMouse(x1, y1);
- }
- default:
- break;
- }
- // Recombining
- if (this.cells.length > 1) {
- var r = 0;
- // Get amount of cells that can merge
- for (var i in this.cells) {
- if (this.cells[i].shouldRecombine) {
- r++;
- }
- }
- // Merge
- if (r >= 2) {
- this.updateMouse(this.centerPos.x, this.centerPos.y);
- }
- }
- };
- BotPlayer.prototype.updateMouse = function(xPos, yPos) {
- this.targetPos = {
- x: xPos,
- y: yPos
- },
- this.mouse = {
- x: this.targetPos.x,
- y: this.targetPos.y
- };
- };
- BotPlayer.prototype.avoidObstacles = function(cell, angle) {
- // Sum up all of the vector angles of obstacles to cell and react against it
- var angleSum = 0;
- var collided = this.collisionFromList(cell, this.obstacles);
- if (collided.length == 0) return angle; // Not to return NaN
- for (var i = 0; i < collided.length; i++) {
- angleSum += this.angle(cell.position, collided[i].position);
- }
- angleSum /= collided.length; // Average out the angle sum
- // Find closest available edge
- angleSum += 1.57;
- return angle + angleSum;
- };
- BotPlayer.prototype.collisionFromList = function(cell, list) {
- var collided = [];
- for (var i = 0; i < list.length; i++) {
- var check = list[i];
- if (this.edgeDistance(cell, check) < 0) collided.push(check);
- }
- return collided;
- };
- BotPlayer.prototype.clearLists = function() {
- this.predators = [];
- this.threats = [];
- this.splitThreats = [];
- this.prey = [];
- this.food = [];
- this.ejectedMass = [];
- this.virus = [];
- this.obstacles = [];
- };
- // Library of subfunctions which are directions to what should I do
- BotPlayer.prototype.getCheckStatus = function(cell, check) {
- // 0 - Prey, 1 - Predator, 2 - Threat, 3 - Split threat, 4 - Obstacle
- var dist = this.distance(cell.position, check.position);
- if (this.isVirus(check)) {
- // Check collision distance
- if (cell.mass / 1.3 > check.mass) {
- return 4; // Obstacle - virus
- }
- } else if (this.isMyTeam(check)) {
- // Check collision distance
- if (dist < this.collideDistance(cell, check) * 1.2) {
- return 4; // Obstacle - same team cell
- }
- } else {
- var eat = this.checkEat(cell, check);
- if (eat == 1) return 0; // Prey
- if (eat == -1) {
- // If I am too close to it, I should fear it
- // My fear distance should be maximum when the cell wants to splitkill me
- // And be minimum when the cell is a lot of times larger than me
- // I'm going to use Math.sin to do this
- var sizeDiff = check.getSize() - cell.getSize();
- var fearDistance = (Math.sin(Math.min(sizeDiff / 50, Math.PI * 1.5)) + 1) * 300 + 50;
- if (dist < fearDistance) {
- if (this.edgeDistance(cell, check) < 0) this.juke = true; // Run
- return 1; // Predator
- } else {
- if (check.mass / 3.5 > cell.mass || dist < fearDistance * 1.5) return 3; // Split threat
- return 2; // Threat
- }
- }
- }
- return -1; // Nothing
- };
- BotPlayer.prototype.addCellToList = function(cell, check) {
- // Check for cell type and return status
- if (check.owner == cell.owner) {
- // Cannot target/be targeted by owner cells
- return;
- }
- var returnStatus = -1;
- if (check.cellType != 1) returnStatus = this.getCheckStatus(cell, check); // Don't check status for pellets
- switch (check.cellType) {
- case 0: // Player cell
- if (returnStatus == 0) this.prey.push(check);
- if (returnStatus == 1) {
- this.predators.push(check);
- this.threats.push(check);
- this.splitThreats.push(check);
- }
- if (returnStatus == 2) {
- this.threats.push(check);
- this.splitThreats.push(check);
- }
- if (this.returnStatus == 3) this.splitThreats.push(check);
- if (this.returnStatus == 4) this.obstacles.push(check);
- break;
- case 1: // Food
- this.food.push(check);
- break;
- case 2: // Virus
- this.virus.push(check);
- if (returnStatus == 4) this.obstacles.push(check);
- break;
- case 3: // Ejected mass
- if (returnStatus == 0) this.food.push(check);
- break;
- default:
- break;
- }
- };
- BotPlayer.prototype.checkEat = function(cell, check) {
- // -1 It can eat me, 0 Nothing, 1 I can eat it
- var mergedMass = 0;
- var isCheckMerging = false;
- if (check.owner) {
- for (var i = 0; i < check.owner.cells.length; i++) {
- var c = check.owner.cells[i];
- if (c.shouldRecombine) {
- mergedMass += c.mass;
- if (c.nodeId == check.nodeId) isCheckMerging = true;
- }
- }
- }
- if (isCheckMerging) {
- if (mergedMass / 1.3 > cell.mass) return -1;
- if (cell.mass / 1.3 > mergedMass) return 1;
- } else {
- if (check.mass / 1.3 > cell.mass) return -1;
- if (cell.mass / 1.3 > check.mass) return 1;
- }
- // Every eating check has passed
- return 0;
- };
- BotPlayer.prototype.combineVectors = function(list) {
- // Average position of all cells from the list
- if (list.length == 0) return null; // List is empty
- var xs = 0, ys = 0;
- for (var i in list) {
- var check = list[i];
- xs += check.position.x;
- ys += check.position.y;
- }
- xs /= list.length;
- ys /= list.length;
- return {
- x: xs,
- y: ys
- };
- };
- BotPlayer.prototype.findNearbyVirus = function(cell, checkDist, list) {
- var r = cell.getSize() + 100; // Gets radius + virus radius
- for (var i = 0; i < list.length; i++) {
- var check = list[i];
- var dist = this.distance(cell, check) - r;
- if (checkDist > dist) {
- return check;
- }
- }
- return false; // Returns a bool if no nearby viruses are found
- };
- BotPlayer.prototype.bestPrey = function(cell, list) {
- // Finds the best prey to target
- if (list.length == 0) return null; // List is empty
- var best = list[0];
- var bestCoefficient = this.distance(cell.position, best.position) - best.mass * 5;
- for (var i = 1; i < list.length; i++) {
- var check = list[i];
- var checkDist = this.distance(cell.position, check.position);
- var coefficient = checkDist - check.size * 8;
- if (bestCoefficient > coefficient) {
- // Better coefficient
- best = check;
- bestCoefficient = coefficient;
- }
- }
- return best;
- };
- BotPlayer.prototype.shortestPrey = function(cell, list) {
- // Finds the shortest prey to target
- if (list.length == 0) return null; // List is empty
- var distances = [];
- var indexes = [];
- for (var i = 1; i < list.length; i++) {
- var check = list[i];
- distances.push([this.distance(cell, check), check]);
- }
- distances.sort(function(a, b) {
- return b[0] - a[0];
- });
- return distances[0][1];
- };
- BotPlayer.prototype.getBiggest = function(list) {
- // Gets the biggest cell from the array
- var biggest = list[0];
- for (var i = 1; i < list.length; i++) {
- var check = list[i];
- if (check.mass > biggest.mass) {
- biggest = check;
- }
- }
- return biggest;
- };
- BotPlayer.prototype.distance = function(position1, position2) {
- var xs = position1.x - position2.x;
- var ys = position1.y - position2.y;
- return Math.sqrt(xs * xs + ys * ys);
- };
- BotPlayer.prototype.angle = function(position1, position2) {
- var xs = position1.x - position2.x;
- var ys = position1.y - position2.y;
- return Math.atan2(xs, ys);
- };
- BotPlayer.prototype.edgeDistance = function(cell, check) {
- // distance - (r1 + r2)
- return this.distance(cell.position, check.position) - this.collideDistance(cell, check);
- };
- BotPlayer.prototype.isVirus = function(cell) {
- return cell.spiked == 1;
- };
- BotPlayer.prototype.collideDistance = function(cell, check) {
- // r1 + r2
- return cell.getSize() + check.getSize();
- };
- BotPlayer.prototype.isMyTeam = function(check) {
- // Is that cell in my team?
- return this.team == check.owner.team && this.gameServer.gameMode.haveTeams;
- };
- BotPlayer.prototype.canSplitkill = function(cell, check) {
- // Can I split to eat this cell?
- var dist = this.distance(cell.position, check.position);
- var splitDist = this.splitDistance(cell);
- return (cell.mass / 2.6) > check.mass && dist < splitDist;
- };
- BotPlayer.prototype.splitDistance = function(cell) {
- // How long will I go after I have split
- var mass = cell.mass;
- var t = Math.PI * Math.PI;
- var modifier = 3 + Math.log(1 + mass) / 10;
- var splitSpeed = this.gameServer.config.playerSpeed * Math.min(Math.pow(mass, -Math.PI / t / 10) * modifier, 150);
- return Math.max(splitSpeed * 6.8, cell.getSize() * 2); // Checked via C#, final distance is near 6.512x splitSpeed
- };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement