// Cloned by Enhanced on 27 Jun 2018 from World "Cloned Cells" by SinfulSalad
// Please leave this clone trail here.
// Cloned by SinfulSalad on 25 Jun 2018 from World "Cells" by SinfulSalad
// Please leave this clone trail here.
// ============ Cells ===============================================================================================
// 'Cells' is a program that displays 4 team of cells trying to infect each other, until total domination is
// accomplished. The rules are simple : once a cell is possessed by a team, it starts generating value, until a maximum
// value is reached. Each cell can, at any given time, split and send one of its halves to another cell to infect it.
// If the value of this half is higher than the value of the cell it is trying to infect, then the infection will
// succeed. The team that wins is the team that controls all cells
// ========== Possible Improvements ==================================================================================
// The score and the chart is designed to keep track of only 4 teams. Maybe code one that works for any number
// of teams.
//
// With this version, each cell behaves individually, there is no 'teamwork' strategy. Maybe implement a
// a 'hivemind' AI that can control all its cells.
//
// One could code a version of this where the user could actually control one of the team
// ===================================================================================================================
// === Start of tweaker's box ========================================================================================
// ===================================================================================================================
// The easiest things to modify are in this box.
// You should be able to change things in this box without being a JavaScript programmer.
// Go ahead and change some of these. What's the worst that could happen?
// These 3 have default values, so this section is optional:
AB.clockTick = 33;
// Speed of run: Step every n milliseconds. Default 100.
AB.maxSteps = 20000;
// Length of run: Maximum length of run in steps. Default 1000.
AB.screenshotStep = 1000;
// Take screenshot on this step. (All resources should have finished loading.) Default 50.
const SKYCOLOR = "#000000"; // this usage will be of color as a string, not as a number
//You can import your own music here
const MUSIC_BACK = '/uploads/sinfulsalad/Forest.mp3' ;
// ===================================================================================================================
// === End of tweaker's box ==========================================================================================
// ===================================================================================================================
// You will need to be some sort of JavaScript programmer to change things below the tweaker's box.
// ------------------------------ MUSIC ----------------------------------------
var backmusic = new Audio ( MUSIC_BACK );
function initMusic()
{
backmusic.loop = true; // loop forever
backmusic.play();
$("#w2m_audio1").html( " " );
$("#w2m_audio2").html( " " );
}
//---------------------------USEFUL FUNCTIONS-----------------------------------
//take the hexadecimal value of a color 'col', and make it brighter or darker
//depending on 'amt'
function lightenDarkenColor(col, amt) {
var usePound = false;
if (col[0] == "#") {
col = col.slice(1);
usePound = true;
}
var num = parseInt(col,16);
var r = (num >> 16) + amt;
if (r > 255) r = 255;
else if (r < 0) r = 0;
var b = ((num >> 8) & 0x00FF) + amt;
if (b > 255) b = 255;
else if (b < 0) b = 0;
var g = (num & 0x0000FF) + amt;
if (g > 255) g = 255;
else if (g < 0) g = 0;
return (usePound?"#":"") + (g | (b << 8) | (r << 16)).toString(16);
}
//calculate the unsigned value of the angle between two 2D vector
function angle(vect1, vect2)
{
rst = vect1.x * vect2.x + vect1.y * vect2.y;
rst /= (Math.sqrt(Math.pow(vect1.x,2)+Math.pow(vect1.y,2)))*(Math.sqrt(Math.pow(vect2.x,2)+Math.pow(vect2.y,2)));
rst = Math.acos(rst);
return rst;
}
//Search for an intem in an array, and returns its index
//If the item is present muliple times in the array, it will return the first
//occurence of that item
//Returns -1 if the item is missing from the array
function getIndexOfItem(item, array)
{
var index = 0;
while (array[index] != item && index < array.length) index++;
if (index == array.length) index = -1;
return index;
}
//generate a random float between [ A ; B [
function randomfloatAtoB ( A, B )
{
return ( A + ( Math.random() * (B-A) ) );
}
//returns a whole number in from the [A,B[ range
function randomintAtoB ( A, B )
{
return ( Math.floor ( randomfloatAtoB ( A, B ) ) );
}
//---------------end useful functions-----------------------------------------------
function World() {
//Speed of the simulation when it starts
var SPEED = 1;
var timer = 0;
var canvas;
var context;
//define the middle of the drawing area
var centerX;
var centerY;
//contains all the cells from the simulation
var cells = [];
//contains all the "ships" that travels from one cell to an other
var cellShips = [];
//4 instances of the object Team
var teamPink;
var teamGreen;
var teamBlue;
var teamOrange;
//will keep track of the score of each team
var score;
//------------------------ CLASSES DECLARATION -----------------------------
//constructor of Team objects
function Team(name, color, startingCell)
{
var team = Object.create(teamPrototype);
team.name = name;
//hexadecimal value of the color of its cells
team.color = color;
//contains all the cells possessed by the team
team.cells = [];
//give a starting sell to the team, and paint it the right color
startingCell.color = team.color;
team.cells.push(startingCell);
//create an array that contains only the cells that need to be conquered
team.possibleTargets = cells.slice();
team.possibleTargets.splice(getIndexOfItem(startingCell, team.possibleTargets), 1);
return team;
}
var teamPrototype = {
//no functions to be defined at the moment
}
function getTeamByColor(color)
{
switch (color) {
case "pink" :
return teamPink;
case "green" :
return teamGreen;
case "blue" :
return teamBlue;
case "orange" :
return teamOrange;
case "#FE2E9A" :
return teamPink;
case "#80FF00" :
return teamGreen;
case "#58FAF4" :
return teamBlue;
case "#FE642E" :
return teamOrange;
default :
console.log("it failed");
return undefined;
}
}
//constructor of cells
function Cell(x,y, type, color = '#555555', destination = cells)
{
var cell = Object.create(cellPrototype);
//the position (x,y) described here are relative to centerX and centerY,
//which define the center of the drawing area
cell.x = x;
cell.y = y;
//the type of a cell is either 'small', 'average(avg)' or 'huge'
//its type changes its radius, its maximum capacity, and its production
//rate
cell.type = type;
switch (type)
{
case "small" :
cell.radius = 15;
cell.value = 5;
break;
case "avg" :
cell.radius = 25;
cell.value = 10;
break;
default :
cell.radius = 50;
cell.value = 20;
break;
}
cell.maxVal = cell.value*5;
cell.rate = Math.floor(30/cell.maxVal/AB.clockTick/SPEED*1000);
//the default color of each cell is '#555555'
cell.color = color;
//when the cell is created, it is saved in an array.
//the default array is 'cells[]' (which contains all cells)
destination.push(cell);
return cell;
}
var cellPrototype = {
//draw the cell on the canvas :
//at the right position
//with the color of its team
//with its current value written in its center
draw : function() {
context.beginPath();
context.arc(centerX+this.x, centerY+this.y, this.radius, 0, 2 * Math.PI, false);
context.fillStyle = this.color;
context.fill();
context.lineWidth = 2;
context.fillStyle = '#000000';
strokeRGB = lightenDarkenColor(this.color, +100);
context.strokeStyle = strokeRGB;
context.fillText(Math.floor(this.value), centerX+this.x-1, centerY+this.y+5);
context.stroke();
},
//create cells symmetric to this cells (horizontal AND vertical symmetry),
//add them to the array 'cells', and draw them on the canvas.
drawSymmetry : function() {
var newcell = Cell(-this.x, this.y, this.type, this.color);
newcell.draw();
newcell = Cell(-this.x, -this.y, this.type, this.color);
newcell.draw();
newcell = Cell(this.x, -this.y, this.type, this.color);
newcell.draw();
},
//any number of cells, located evenly around this cell.
//you can change what type of cell you want to produce,
//and how far away they should be from this cell
drawSurround : function(nbCells, radius, type) {
var newCell;
for (var i=0 ; i < 2*Math.PI-0.01 ; i += 2*Math.PI/nbCells)
{
newCell = Cell(this.x+radius*Math.cos(i), this.y+radius*Math.sin(i), type);
newCell.draw();
}
},
//calculate the direction a 'ship' needs to move towards in order to
//reach its destination
setDirection : function(cell) {
var vect0 ={
x : 1,
y : 0
};
var trajectory ={
x : cell.x-this.x,
y : cell.y-this.y
};
var ang = angle(vect0, trajectory);
if (cell.y < this.y) ang = -ang;
this.direction = ang;
},
//create a 'ship' from this cell, give half the value of the cell to
//the 'ship', and give it a destination (a cell)
split : function(dest) {
var newCellShip = Cell(this.x, this.y, "small", this.color, cellShips);
newCellShip.value = Math.floor(this.value/2);
newCellShip.dest = dest;
newCellShip.direction;
newCellShip.setDirection(dest);
this.value = Math.floor(this.value/2);
},
//when this 'ship' has reached its destination, it transfers its value
//to the destination cell. If the value of the ship is superior to the
//value of the destination, the infection will work, and the destination
//will become a part of the ship's team
//the arrays of the concerned teams must be updated
infect : function(){
//if close enough
if (Math.abs(this.x-this.dest.x) < 2*SPEED && Math.abs(this.y-this.dest.y) < 2*SPEED)
{
//remove the ship from the array of all ships
cellShips.splice(getIndexOfItem(this, cellShips), 1);
//calculate the resulting value of the destination
if (this.dest.color != this.color) this.dest.value -= this.value;
else this.dest.value += this.value;
//if the ship was strong enough to bring the value of the destination
//below zero, then change the team of the destination
//and update the arrays accordingly
if (this.dest.value < 0)
{
this.dest.value = -this.dest.value;
if (this.dest.color !== "#555555")
{
//delete cell from original team.cell
getTeamByColor(this.dest.color).cells.splice(getIndexOfItem(this.dest,getTeamByColor(this.dest.color).cells), 1);
//add cell to original team.target
getTeamByColor(this.dest.color).possibleTargets.push(this.dest);
}
this.dest.color = this.color;
//delete cell from new team.target
getTeamByColor(this.color).possibleTargets.splice(getIndexOfItem(this.dest,getTeamByColor(this.dest.color).possibleTargets), 1);
//add cell to new team.cell
getTeamByColor(this.color).cells.push(this.dest);
}
if (this.dest.value > this.dest.maxVal) this.dest.value = this.dest.maxVal;
}
},
//describe how each cell behave
//there is no hivemind in this version of the world
individualMind : function(){
var rand;
//the more a huge cell is close to its max value, the more it is
//likely to split and infect other cells
//all targets are chosen randomly between enemies or neutral cells
if (this.type == "huge")
{
rand = (this.maxVal - this.value);
if (randomintAtoB(0, rand) === 0 && getTeamByColor(this.color).possibleTargets.length !== 0)
{
rand = randomintAtoB(0, getTeamByColor(this.color).possibleTargets.length);
this.split(getTeamByColor(this.color).possibleTargets[rand]);
}
}
//whenever a small or average cell reaches it's max value, it will split
//all targets are chosen randomly between enemies or neutral cells
else if ((this.type == "small" || this.type == "avg") && this.value == this.maxVal)
{
if (getTeamByColor(this.color).possibleTargets.length !== 0)
{
rand = randomintAtoB(0, getTeamByColor(this.color).possibleTargets.length);
this.split(getTeamByColor(this.color).possibleTargets[rand]);
}
}
}
};
//-------------------------- INIT FUNCTIONS --------------------------------
function initContext()
{
context = canvas.getContext('2d');
context.font = "bold 15px Arial";
context.textAlign = 'center';
}
//Use the cell constructor to create the 'battlefield'
//remember that all cells created are added to the array 'cells'
function initStage()
{
var newCell = Cell(200, 200, "huge");
newCell.drawSurround(6, 90, "small");
newCell = Cell(500, 70, "huge");
newCell = Cell(350, 80, "avg");
newCell.drawSurround(3, 60, "small");
newCell = Cell(90*Math.cos(Math.PI/8), 90*Math.sin(Math.PI/8), "avg");
newCell = Cell(90*Math.cos(3*Math.PI/8), 90*Math.sin(3*Math.PI/8), "avg");
var length = cells.length;
for (var i=0 ; i