sandboxels/mods/generative_mods.js

2506 lines
92 KiB
JavaScript

var modName = "mods/generative_mods.js";
var explodeAtPlusMod = "mods/explodeAtPlus.js";
var runAfterAutogenMod = "mods/runAfterAutogen and onload restructure.js";
var libraryMod = "mods/code_library.js";
var feyAndMoreMod = "mods/fey_and_more.js";
var mobsMod = "mods/mobs.js";
if(enabledMods.includes(runAfterAutogenMod) && enabledMods.includes(explodeAtPlusMod) && enabledMods.includes(libraryMod) && enabledMods.includes(feyAndMoreMod) && enabledMods.includes(mobsMod)) {
//urlParams reads
//Bombs
if(urlParams.get('generateBombs') !== null) { //if the variable exists at all
generateBombs = true
} else { //if it doesn't (and it returns null)
generateBombs = false
}
if(urlParams.get('bombAmount') != null) { //null check
bombAmount = urlParams.get('bombAmount')
if(isNaN(bombAmount) || bombAmount === "" || bombAmount === null) { //NaN check
bombAmount = 10
}
bombAmount = parseInt(bombAmount)
if(bombAmount > 50) {
alert("Maximum amount of additional bomb/anti-bomb pairs is 50.\nOnly 50 were added.")
} else if(bombAmount < 1) {
alert("Minimum amount of additional bomb/anti-bomb pairs is 1.\n1 pair was added.")
}
bombAmount = Math.min(50,Math.max(bombAmount,1))
} else {
bombAmount = 10
}
//Clouds
if(urlParams.get('generateClouds') !== null) { //if the variable exists at all
generateClouds = true
} else { //if it doesn't (and it returns null)
generateClouds = false
}
if(urlParams.get('cloudIncludeRandom') !== null) { //if the variable exists at all
cloudIncludeRandom = true
} else { //if it doesn't (and it returns null)
cloudIncludeRandom = false
}
//Creepers
//Include generated creepers in Random tool?
if(urlParams.get('creeperIncludeRandom') !== null) { //if the variable exists at all
creeperIncludeRandom = true
} else { //if it doesn't (and it returns null)
creeperIncludeRandom = false
}
//Generate creepers
if(urlParams.get('generateCreepers') !== null) { //if the variable exists at all
generateCreepers = true
} else { //if it doesn't (and it returns null)
generateCreepers = false
}
//Fairies
if(urlParams.get('fairyIncludeRandom') !== null) { //if the variable exists at all
fairyIncludeRandom = true
} else { //if it doesn't (and it returns null)
fairyIncludeRandom = false
}
//Generate fairies
if(urlParams.get('generateFairies') !== null) { //if the variable exists at all
generateFairies = true
} else { //if it doesn't (and it returns null)
generateFairies = false
}
if(urlParams.get('fairyAmount') != null) { //null check
fairyAmount = urlParams.get('fairyAmount')
if(isNaN(fairyAmount) || fairyAmount === "" || fairyAmount === null) { //NaN check
fairyAmount = 10
}
fairyAmount = parseInt(fairyAmount)
if(fairyAmount > 10000) {
alert("Maximum amount of additional fairies is 10000.\nOnly 10000 fairies were added.")
} else if(fairyAmount < 1) {
alert("Minimum amount of additional fairies is 1.\n1 fairy was added.")
}
fairyAmount = Math.min(10000,Math.max(fairyAmount,1))
} else {
fairyAmount = 10
}
//Spouts
//Generate spouts
if(urlParams.get('generateSpouts') !== null) { //if the variable exists at all
generateSpouts = true
} else { //if it doesn't (and it returns null)
generateSpouts = false
}
if(urlParams.get('spoutIncludeRandom') !== null) { //if the variable exists at all
spoutIncludeRandom = true
} else { //if it doesn't (and it returns null)
spoutIncludeRandom = false
}
//Requisite variables
//Bombs
amalgamatedBombFire = "plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,plasma,smoke,plasma,plasma,fire,smoke,fire,smoke,plasma,metal_scrap,metal_scrap,metal_scrap,metal_scrap,metal_scrap,acid,acid,oil,oil,oil,oil,oil,oil,oil,plasma,plasma,plasma,plasma,plasma,smoke,plasma,plasma,fire,smoke,plasma,metal_scrap,metal_scrap,metal_scrap,metal_scrap,metal_scrap,metal_scrap,flash,flash,flash,flash,flash,acid_gas,acid_gas,acid_gas,acid,oil,oil,oil,oil,oil,oil,oil,oil,oil,oil,plasma,plasma,plasma,plasma,metal_scrap,metal_scrap,metal_scrap,metal_scrap,metal_scrap,acid,acid,oil,oil,oil,oil,oil,oil,oil,plasma,plasma,plasma,plasma,plasma,smoke,plasma,plasma,fire,smoke,plasma,metal_scrap,metal_scrap,metal_scrap,metal_scrap,metal_scrap,metal_scrap,electric_cluster_bomb,electric_cluster_bomb,flash,flash,flash,flash,flash,acid_gas,acid_gas,acid_gas,acid,oil,oil,oil,oil,oil,oil,oil,oil,oil,oil,plasma,plasma,plasma,plasma,plague,plague,plague,plague,plague,plague,radiation,radiation,radiation,radiation,radiation,radiation,radiation,radiation,uranium,uranium,uranium,uranium,uranium,uranium,greek_fire,greek_fire,greek_fire,greek_fire,greek_fire,antimatter,antimatter,antimatter,antimatter,antimatter,smoke_grenade,antimatter,smoke_grenade,fireball,flash,acid_gas,acid_gas,acid_gas,plague,plague,plague,plague,plague,plague,radiation,radiation,radiation,radiation,radiation,radiation,radiation,radiation,uranium,uranium,uranium,uranium,uranium,uranium,greek_fire,greek_fire,greek_fire,greek_fire,greek_fire,antimatter,antimatter,antimatter,antimatter,antimatter,smoke_grenade,antimatter,flash,acid_gas,acid_gas,acid_gas,radiation,radiation,radiation,radiation,plague,acid_gas,acid_gas,acid_gas,chlorine,chlorine,chlorine"
eLists.BOMB = ["bomb", "tnt", "c4", "grenade", "dynamite", "gunpowder", "firework", "nuke", "h_bomb", "dirty_bomb", "emp_bomb", "sticky_bomb", "cold_bomb", "hot_bomb", "electro_bomb", "water_bomb", "antimatter_bomb", "flashbang", "smoke_grenade", "fireball", "landmine", "cluster_bomb", "cluster_nuke", "op_hottester_bomb", "anti-bomb", "electric_bomblet", "electric_cluster_bomb", "radioactive_popper", "acid_bomb", "amalgamated_bomb"]; bombChoices = eLists.BOMB;
var excludedBombElements = ["water", "antimatter", "acid"];
//Clouds
eLists.CLOUD = ["cloud", "rain_cloud", "snow_cloud", "fire_cloud", "hail_cloud", "acid_cloud", "pyrocumulus"]; cloudChoices = eLists.CLOUD;
var includedClouds = ["cloud", "rain_cloud", "snow_cloud", "fire_cloud", "hail_cloud", "acid_cloud", "pyrocumulus"];
var excludedCloudElements = ["snow", "fire", "hail", "acid"];
if(typeof(behaviorGenerators) === "undefined") { behaviorGenerators = {} };
//Creepers
var placeholderColor = "#FF00FF";
var hslOffsets = [[0, -5, 5], [0, -20, 10], [0, 0, 10], [0, -20, 10], [0, -35, 0], [0, -20, -30], [0, 10, -10], [0, 10, 20], [0, -20, 10], [0, -10, 5]];
var colorOfRandomCreeper = ["#7ba883", "#8aba8a", "#87b292", "#8aba8a", "#71a171", "#346434", "#4d6d72", "#a0caad", "#8aba8a", "#7dac7f"]
//Fairies
var excludedFairyElements = []
var backupCategoryWhitelist = ["land","powders","weapons","food","life","corruption","states","fey","Fantastic Creatures","dyes","energy liquids","random liquids","random gases","random rocks"];
var backupElementWhitelist = ["mercury", "chalcopyrite_ore", "chalcopyrite_dust", "copper_concentrate", "fluxed_copper_concentrate", "unignited_pyrestone", "ignited_pyrestone", "everfire_dust", "extinguished_everfire_dust", "mistake", "polusium_oxide", "vaporized_polusium_oxide", "glowstone_dust", "redstone_dust", "soul_mud", "wet_soul_sand", "nitrogen_snow", "fusion_catalyst", "coal", "coal_coke", "blast_furnace_fuel", "molten_mythril"];
//Spouts
eLists.SPOUT = ["spout", "udder", "torch"]; spoutChoices = eLists.SPOUT;
var excludedSpoutElements = ["ketchup", "liquid_cloner", "fire_cloner"]
var includedSpouts = ["ketchup_spout", "spout", "udder", "torch"]
var backupCategoryWhitelist = ["land","powders","weapons","food","life","corruption","states","fey","Fantastic Creatures","dyes","energy liquids","random liquids","random gases","random rocks"];
var backupElementWhitelist = ["mercury", "chalcopyrite_ore", "chalcopyrite_dust", "copper_concentrate", "fluxed_copper_concentrate", "unignited_pyrestone", "ignited_pyrestone", "everfire_dust", "extinguished_everfire_dust", "mistake", "polusium_oxide", "vaporized_polusium_oxide", "glowstone_dust", "redstone_dust", "soul_mud", "wet_soul_sand", "nitrogen_snow", "fusion_catalyst", "coal", "coal_coke", "blast_furnace_fuel", "molten_mythril"];
//Requisite non-generating functions
//Bombs
function firebombFire(pixel,x,y,radius,fire,smoke,power,damage) {
var coords = circleCoords(pixel.x,pixel.y,radius);
for (var i = 0; i < coords.length; i++) {
var x = coords[i].x;
var y = coords[i].y;
if(!isEmpty(x,y,true)) {
var pixel = pixelMap[x][y];
var info = elements[pixel.element];
var cursedFireChance = 0.15 + power;
if (info.burn) { //Light everything on fire
pixel.burning = true;
pixel.burnStart = pixelTicks;
pixel.temp += 10; //smoke prevention
} else if(Math.random() < cursedFireChance) { //(15+power)%/px cursed burning
pixel.burning = true;
pixel.burnStart = pixelTicks;
pixel.temp += 10;
};
} else if(isEmpty(x,y)) { //if there's space for fire
if (Array.isArray(fire)) { //this should remain "fire"
var newfire = fire[Math.floor(Math.random() * fire.length)];
} else {
var newfire = fire;
};
createPixel(newfire,x,y); //add fire
var firePixel = pixelMap[x][y];
firePixel.temp = Math.max(elements[newfire].temp,firePixel.temp);
firePixel.burning = true;
};
};
};
function hotterBomb(pixel,x,y,radius,fire,smoke,power,damage) {
//console.log(`Radius: ${radius}\nPower: ${power}\nPixel: (${pixel.x},${pixel.y})\nDamage: ${damage}`);
//console.log(`Expected temperature increase for pixel at (${pixel.x},${pixel.y}): ${800 * ((1 + (7 * damage)) ** 2) * ((power ** 2) * 1.5)}`);
pixel.temp += (800 * ((1 + (7 * damage)) ** 2) * ((power ** 2) * 1.5));
};
function empCharge(pixel,x,y,radius,fire,smoke,power,damage) {
var info = elements[pixel.element];
if(info.conduct) {
var distanceFromEdge = Math.max(0,radius - coordPyth(pixel.x,pixel.y,x,y));
var radiusRelatedIncrease = Math.sqrt(distanceFromEdge/5);
pixel.charge ??= 0;
pixel.charge += (50 * radiusRelatedIncrease);
pixel.temp += 700/bound(info.conduct,0.1,1);
};
};
function starbombHeat(pixel,x,y,radius,fire,smoke,power,damage) { //Massively heats depending on distance from explosion center
var distanceFromEdge = Math.max(0,radius - coordPyth(pixel.x,pixel.y,x,y));
var radiusRelatedIncrease = 10 ** logN(distanceFromEdge,5);
pixel.temp += (10000 * radiusRelatedIncrease);
pixelTempCheck(pixel);
};
//Clouds
behaviorGenerators.cloud = function(element) {
if(element instanceof Array) { element = element.join(",") };
return [
["XX","XX","XX"],
["XX",`CH:${element}%0.05`,"M1%2.5 AND BO"],
["XX","XX","XX"]
];
};
behaviorGenerators.heavy_cloud = function(element) {
if(element instanceof Array) { element = element.join(",") };
return [
["XX","XX","XX"],
["XX",`CH:${element}%0.1`,"M1%2.5 AND BO"],
["XX",`CR:${element}%0.05`,"XX"]
];
};
behaviorGenerators.heavier_cloud = function(element) {
if(element instanceof Array) { element = element.join(",") };
return [
["XX","XX","XX"],
["XX",`CH:${element}%0.2`,"M1%2.5 AND BO"],
["XX",`CR:${element}%0.1`,"XX"]
];
};
behaviorGenerators.heaviest_cloud = function(element) {
if(element instanceof Array) { element = element.join(",") };
return [
["XX","XX","XX"],
["XX",`CH:${element}%0.4`,"M1%2.5 AND BO"],
["XX",`CR:${element}%0.2`,"XX"]
];
};
behaviorGenerators.heaviester_cloud = function(element) {
if(element instanceof Array) { element = element.join(",") };
return [
["XX","XX","XX"],
["XX",`CH:${element}%0.8`,"M1%2.5 AND BO"],
["XX",`CR:${element}%0.4`,"XX"]
];
};
behaviorGenerators.heaviestest_cloud = function(element) {
if(element instanceof Array) { element = element.join(",") };
return [
["XX",`CR:${element}%0.8`,"XX"],
[`CR:${element}%0.05`,`CH:${element}%3`,`CR:${element}%0.05 AND M1%2.5 AND BO`],
["XX",`CR:${element}%0.8`,"XX"]
];
};
function placeDownwardColumn(element,xx,yy,creationChance) {
if(element.includes(",")) { element = element.split(",") };
var newElement = element;
//console.log(JSON.stringify(element));
//console.log("from " + xx + "," + yy)
if(!outOfBounds(xx,yy)) {
for(i = yy; i < pixelMap[xx].length; i++) {
if(Array.isArray(element)) {
newElement = element[Math.floor(Math.random() * element.length)];
};
//console.log(JSON.stringify(newElement));
if(isEmpty(xx,i,false)) {
if(Math.random() < creationChance) {
createPixel(newElement,xx,i)
}
} else if(!isEmpty(xx,i,true)) {
var newPixel = pixelMap[xx][i];
var otherElement = newPixel.element;
var newState = elements[otherElement].state ?? "solid"; //assume undefined = solid
if(newState !== "solid") {
continue;
} else {
break;
};
} else if(outOfBounds(xx,i)) {
break;
};
};
};
};
function singleColumn(pixel,element,chance,fillAmountDecimal) {
if(Math.random() < chance) {
placeDownwardColumn(element,pixel.x,pixel.y+1,fillAmountDecimal);
}
};
function multiColumn(pixel,element,chance,fillAmountDecimal,columnRadius) {
if(Math.random() < chance) {
for(j = 0 - columnRadius; j <= columnRadius; j++) {
placeDownwardColumn(element,pixel.x+j,pixel.y+1,fillAmountDecimal);
};
}
};
//Creepers
autoCreeperPlacerTick = function(pixel) {
var creeperElement = elements[pixel.element].creeperType;
var headName,bodyName;
if(typeof(creeperElement === "string")) { //comma separated string check
if(creeperElement.includes(",")) { //if it is
creeperElement = creeperElement.split(","); //to array
creeperElement = creeperElement.filter(function(e) { //strip nonexistent elements
return typeof(elements[e]) === "object";
});
};
};
if(Array.isArray(creeperElement)) {
headName = `${creeperElement.join("_")}_creeper_head`; //auto head element name
bodyName = `${creeperElement.join("_")}_creeper_body`; //auto body element name
} else {
headName = `${creeperElement}_creeper_head`; //auto head element name
bodyName = `${creeperElement}_creeper_body`; //auto body element name
};
if (isEmpty(pixel.x, pixel.y+1)) {
createPixel(bodyName, pixel.x, pixel.y+1);
pixel.element = headName;
pixel.color = pixelColorPick(pixel)
} else if (isEmpty(pixel.x, pixel.y-1)) {
createPixel(headName, pixel.x, pixel.y-1);
pixel.element = bodyName;
pixel.color = pixelColorPick(pixel)
} else {
deletePixel(pixel.x, pixel.y);
};
};
autoCreeperBodyTick = function(pixel) {
var creeperElement = elements[pixel.element].creeperType;
var headName,bodyName,explodeInto;
if(typeof(creeperElement === "string")) { //comma separated string check
if(creeperElement.includes(",")) { //if it is
creeperElement = creeperElement.split(","); //to array
creeperElement = creeperElement.filter(function(e) { //strip nonexistent elements
return typeof(elements[e]) === "object";
});
};
};
if(Array.isArray(creeperElement)) {
headName = `${creeperElement.join("_")}_creeper_head`; //auto head element name
bodyName = `${creeperElement.join("_")}_creeper_body`; //auto body element name
explodeInto = creeperElement.join(","); //auto body element name
} else {
headName = `${creeperElement}_creeper_head`; //auto head element name
bodyName = `${creeperElement}_creeper_body`; //auto body element name
explodeInto = creeperElement; //auto body element name
};
if (tryMove(pixel, pixel.x, pixel.y+1)) { // Fall
if (!isEmpty(pixel.x, pixel.y-2, true)) { // Drag head down
var headPixel = pixelMap[pixel.x][pixel.y-2];
if (headPixel.element == headName) {
if (isEmpty(pixel.x, pixel.y-1)) {
movePixel(pixelMap[pixel.x][pixel.y-2], pixel.x, pixel.y-1);
}
else {
swapPixels(pixelMap[pixel.x][pixel.y-2], pixelMap[pixel.x][pixel.y-1]);
}
}
}
}
doHeat(pixel);
doBurning(pixel);
doElectricity(pixel);
if (pixel.dead) {
// Turn into rotten_meat if pixelTicks-dead > 500
if (pixelTicks-pixel.dead > 200) {
Math.random() < 0.1 ? changePixel(pixel,"gunpowder") : changePixel(pixel,"rotten_meat");
}
return
}
// Find the head
if (!isEmpty(pixel.x, pixel.y-1, true) && pixelMap[pixel.x][pixel.y-1].element == headName) {
var head = pixelMap[pixel.x][pixel.y-1];
if (head.dead) { // If head is dead, kill body
pixel.dead = head.dead;
}
}
else { var head = null }
if (isEmpty(pixel.x, pixel.y-1)) {
// create blood if decapitated 10% chance
if (Math.random() < 0.1) {
createPixel("blood", pixel.x, pixel.y-1);
// set dead to true 15% chance
if (Math.random() < 0.15) {
pixel.dead = pixelTicks;
}
}
}
else if (head == null) { return }
else if (Math.random() < 0.1) { // Move 10% chance
var movesToTry = [
[1*pixel.dir,0],
[1*pixel.dir,-1],
];
// While movesToTry is not empty, tryMove(pixel, x, y) with a random move, then remove it. if tryMove returns true, break.
while (movesToTry.length > 0) {
var move = movesToTry.splice(Math.floor(Math.random() * movesToTry.length), 1)[0];
if (isEmpty(pixel.x+move[0], pixel.y+move[1]-1)) {
if (tryMove(pixel, pixel.x+move[0], pixel.y+move[1])) {
movePixel(head, head.x+move[0], head.y+move[1]);
break;
};
};
};
// 15% chance to change direction while not chasing a human
if(!head.following) {
if (Math.random() < 0.15) {
pixel.dir *= -1;
//console.log("*turns around cutely to face ${pixel.dir < 0 ? 'left' : 'right'}*");
};
}/* else {
//console.log("*chases cutely*");
};*/
};
if(pixel.charge) {
pixel.charged = true;
};
if(head) {
if(typeof(head.charge) !== "undefined") {
if(head.charge) {
pixel.charged = true;
};
};
if(typeof(head.charged) !== "undefined") {
if(head.charged) {
pixel.charged = true;
};
};
};
if(typeof(pixel.charged) === "undefined") {
pixel.charged = false;
};
if(pixel.charged) {
var explosionRadius = 7;
if(!pixel.didChargeBlueTinted) { //do once, on initial charge
//console.log("something something halsey lyric");
var color = pixel.color;
if(color.startsWith("rgb")) {
//console.log("rgb detected");
color = color.split(","); //split color for addition
var red = parseFloat(color[0].substring(4));
var green = parseFloat(color[1]);
var blue = parseFloat(color[2].slice(0,-1));
red = rgbColorBound(red + 51);
green = rgbColorBound(green + 51);
blue = rgbColorBound(blue + 102);
color = `rgb(${red},${green},${blue})`;
pixel.color = color;
//console.log("color set");
} else if(color.startsWith("hsl")) {
//console.log("hsl detected");
color = color.split(","); //split color for addition
var hue = parseFloat(color[0].substring(4));
var saturation = parseFloat(color[1].slice(0,-1));
var luminance = parseFloat(color[2].slice(0,-2));
hue = hue % 360; //piecewise hue shift
if(hue <= 235 && hue >= 135) {
hue = 185;
} else if(hue < 135) {
hue += 50;
} else if(hue > 235 && hue < 360) {
hue -= 50;
};
saturation = slBound (saturation + 10);
luminance = slBound(luminance + 20);
color = `hsl(${hue},${saturation}%,${luminance}%)`;
pixel.color = color;
//console.log("color set");
};
pixel.didChargeBlueTinted = true;
};
} else {
var explosionRadius = 5;
};
if(pixel.burning) {
pixel.hissing = true;
if(!pixel.hissStart) {
pixel.hissStart = pixelTicks;
};
if(!pixel.burnStart) { //I don't like errors.
pixel.burnStart = pixel.ticks;
};
if(pixelTicks - pixel.burnStart > 30) {
//console.log("Kaboom?");
explodeAt(pixel.x,pixel.y,explosionRadius,creeperElement);
//console.log("Yes, Rico, kaboom.");
};
};
//Head hissing color handler: keeps track of head's hissing for coloring purposes
for(i = 0; i < 1; i++) { //dummy for loop
if(pixel.dead || !head || head.dead) { //can't hiss without a head according to the classic creeper anatomy
//console.log("ss-- oof");
pixel.hissing = false;
break;
};
if(head.hissing) {
//console.log("Ssssssss");
if(!head.hissStart) {
//console.log("t-30 ticks or whatever it was");
head.hissStart = pixelTicks;
};
//Color code {
var ticksHissing = pixelTicks - head.hissStart;
var color = pixel.color; //do on each hissing tick
if(color.startsWith("rgb")) {
//console.log("rgb detected");
color = color.split(","); //split color for addition
var red = parseFloat(color[0].substring(4));
var green = parseFloat(color[1]);
var blue = parseFloat(color[2].slice(0,-1));
red = rgbColorBound(red + ticksHissing);
green = rgbColorBound(green + ticksHissing);
blue = rgbColorBound(blue + ticksHissing);
color = `rgb(${red},${green},${blue})`;
pixel.color = color;
//console.log("color set");
} else if(color.startsWith("hsl")) {
//console.log("hsl detected");
color = color.split(","); //split color for addition
var hue = parseFloat(color[0].substring(4));
var saturation = parseFloat(color[1].slice(0,-1));
var luminance = parseFloat(color[2].slice(0,-2));
//console.log("the j");
luminance = slBound(luminance + 1.176);
//console.log(luminance);
color = `hsl(${hue},${saturation}%,${luminance}%)`;
pixel.color = color;
//console.log("color set");
};
//}
};
};
};
autoCreeperHeadTick = function(pixel) {
var creeperElement = elements[pixel.element].creeperType;
var headName,bodyName,explodeInto;
if(typeof(creeperElement === "string")) { //comma separated string check
if(creeperElement.includes(",")) { //if it is
creeperElement = creeperElement.split(","); //to array
creeperElement = creeperElement.filter(function(e) { //strip nonexistent elements
return typeof(elements[e]) === "object";
});
};
};
if(Array.isArray(creeperElement)) {
headName = `${creeperElement.join("_")}_creeper_head`; //auto head element name
bodyName = `${creeperElement.join("_")}_creeper_body`; //auto body element name
explodeInto = creeperElement.join(","); //auto body element name
} else {
headName = `${creeperElement}_creeper_head`; //auto head element name
bodyName = `${creeperElement}_creeper_body`; //auto body element name
explodeInto = creeperElement; //auto body element name
};
doHeat(pixel);
doBurning(pixel);
doElectricity(pixel);
if (pixel.dead) {
// Turn into rotten_meat if pixelTicks-dead > 500
if (pixelTicks-pixel.dead > 200) {
Math.random() < 0.1 ? changePixel(pixel,"gunpowder") : changePixel(pixel,"rotten_meat");
return
}
}
// Find the body
if (!isEmpty(pixel.x, pixel.y+1, true) && pixelMap[pixel.x][pixel.y+1].element == bodyName) {
var body = pixelMap[pixel.x][pixel.y+1];
if (body.dead) { // If body is dead, kill head
pixel.dead = body.dead;
}
}
else { var body = null }
if(body) {
if(body.dir !== pixel.dir) { //hacky workaround: lock head dir to body dir
pixel.dir = body.dir;
};
};
if (isEmpty(pixel.x, pixel.y+1)) {
tryMove(pixel, pixel.x, pixel.y+1);
// create blood if severed 10% chance
if (isEmpty(pixel.x, pixel.y+1) && !pixel.dead && Math.random() < 0.1) {
createPixel("blood", pixel.x, pixel.y+1);
// set dead to true 15% chance
if (Math.random() < 0.15) {
pixel.dead = pixelTicks;
}
}
}
//start of most new code
var pX = pixel.x;
var pY = pixel.y;
if(pixel.charge) {
pixel.charged = true;
};
if(body) {
if(typeof(body.charge) !== "undefined") {
if(body.charge) {
pixel.charged = true;
};
};
if(typeof(body.charged) !== "undefined") {
if(body.charged) {
pixel.charged = true;
};
};
};
if(typeof(pixel.charged) === "undefined") {
pixel.charged = false;
};
if(pixel.charged) {
var explosionRadius = 10;
if(!pixel.didChargeBlueTinted) { //do once, on initial charge
//console.log("something something halsey lyric");
var color = pixel.color;
if(color.startsWith("rgb")) {
//console.log("rgb detected");
color = color.split(","); //split color for addition
var red = parseFloat(color[0].substring(4));
var green = parseFloat(color[1]);
var blue = parseFloat(color[2].slice(0,-1));
red = rgbColorBound(red + 51);
green = rgbColorBound(green + 51);
blue = rgbColorBound(blue + 102);
color = `rgb(${red},${green},${blue})`;
pixel.color = color;
//console.log("color set");
} else if(color.startsWith("hsl")) {
//console.log("hsl detected");
color = color.split(","); //split color for addition
var hue = parseFloat(color[0].substring(4));
var saturation = parseFloat(color[1].slice(0,-1));
var luminance = parseFloat(color[2].slice(0,-2));
hue = hue % 360; //piecewise hue shift
if(hue <= 235 && hue >= 135) {
hue = 185;
} else if(hue < 135) {
hue += 50;
} else if(hue > 235 && hue < 360) {
hue -= 50;
};
saturation = slBound (saturation + 10);
luminance = slBound(luminance + 20);
color = `hsl(${hue},${saturation}%,${luminance}%)`;
pixel.color = color;
//console.log("color set");
};
pixel.didChargeBlueTinted = true;
};
} else {
var explosionRadius = 7;
};
//Human detection loop (looks ahead according to direction and sets the "following" variable to true, telling the body to lock the direction)
var directionAdverb = "left";
if(pixel.dir > 0) {
directionAdverb = "right";
};
//console.log(`Looking ${directionAdverb}`)
if(pixel.dir === -1) {
for(i = -4; i < 4+1; i++) {
var oY = i;
//console.log(`Starting row look at row ${pY+oY}`)
for(j = (-1); j > (-16 - 1); j--) {
var oX = j;
var nX = pX+oX;
var nY = pY+oY;
if(outOfBounds(nX,nY)) {
//console.log(`Stopping row look at pixel (${nX},${nY}) due to OoB`)
break;
};
if(isEmpty(nX,nY)) {
////console.log(`Skipping pixel (${nX},${nY}) (empty)`)
continue;
};
if(!isEmpty(nX,nY,true)) {
var newPixel = pixelMap[nX][nY];
var newElement = newPixel.element;
if(enemyHumanoidArray.includes(newElement)) {
//console.log(`Human part found at (${nX},${nY})`)
if(!newPixel.dead) {
pixel.following = true;
//console.log(`Human detected at (${nX},${nY})`)
//Start "hissing" if a human is close enough
if(coordPyth(pX,pY,nX,nY) <= 3.15) { //probably misapplying the tolerance from the MC Wiki line: "Creepers will chase after any player, as long as it is within a 16 block (±5%) radius"
pixel.hissing = true;
if(!pixel.hissStart) {
pixel.hissStart = pixelTicks;
};
};
};
} else {
//console.log(`Stopping row look at pixel (${nX},${nY}) due to non-human pixel in the way`)
break; //can't see through humans
};
};
};
};
} else if(pixel.dir === 1) {
for(i = -4; i < 4+1; i++) {
var oY = i;
//console.log(`Starting row look at row ${pY+oY}`)
for(j = 1; j < 16 + 1; j++) {
var oX = j;
var nX = pX+oX;
var nY = pY+oY;
if(outOfBounds(nX,nY)) {
//console.log(`Stopping row look at pixel (${nX},${nY}) due to OoB`)
break;
};
if(isEmpty(nX,nY)) {
////console.log(`Skipping pixel (${nX},${nY}) (empty)`)
continue;
};
if(!isEmpty(nX,nY,true)) {
var newPixel = pixelMap[nX][nY];
var newElement = newPixel.element;
if(enemyHumanoidArray.includes(newElement)) {
//console.log(`Human part found at (${nX},${nY})`)
if(!newPixel.dead) {
pixel.following = true;
//console.log(`Human detected at (${nX},${nY})`)
//Start "hissing" if a human is close enough
if(coordPyth(pX,pY,nX,nY) <= 3.15) {
pixel.hissing = true;
if(!pixel.hissStart) {
pixel.hissStart = pixelTicks;
};
};
break;
};
} else {
//console.log(`Stopping row look at pixel (${nX},${nY}) due to non-human pixel in the way`)
break;
};
};
};
};
};
//Pre-explosion handler: keeps track of time before the kaboom
for(i = 0; i < 1; i++) { //dummy for loop
if(pixel.hissing) {
//console.log("Ssssssss");
if(pixel.dead || !body || body.dead) { //can't explode without a body according to the classic creeper anatomy
//console.log("ss-- oof");
pixel.hissing = false;
break;
};
if(!pixel.hissStart) {
//console.log("t-30 ticks or whatever it was");
pixel.hissStart = pixelTicks;
};
//Color code {
var ticksHissing = pixelTicks - pixel.hissStart;
var color = pixel.color; //do on each hissing tick
if(color.startsWith("rgb")) {
//console.log("rgb detected");
color = color.split(","); //split color for addition
var red = parseFloat(color[0].substring(4));
var green = parseFloat(color[1]);
var blue = parseFloat(color[2].slice(0,-1));
red = rgbColorBound(red + ticksHissing);
green = rgbColorBound(green + ticksHissing);
blue = rgbColorBound(blue + ticksHissing);
color = `rgb(${red},${green},${blue})`;
pixel.color = color;
//console.log("color set");
} else if(color.startsWith("hsl")) {
//console.log("hsl detected");
color = color.split(","); //split color for addition
var hue = parseFloat(color[0].substring(4));
var saturation = parseFloat(color[1].slice(0,-1));
var luminance = parseFloat(color[2].slice(0,-2));
luminance = slBound(luminance + 1.176);
color = `hsl(${hue},${saturation}%,${luminance}%)`;
pixel.color = color;
//console.log("color set");
};
//}
if(pixelTicks - pixel.hissStart > 30) {
//console.log("Kaboom?");
//console.log(`Exploding with element ${creeperElement} and radius ${explosionRadius} (charged: ${pixel.charged})`);
explodeAt(body.x,body.y,explosionRadius,explodeInto);
//console.log("Yes, Rico, kaboom.");
};
};
};
if(Math.random() < 0.01) { //1% chance each tick to lose interest
pixel.following = false;
//console.log("Meh.");
};
};
//Several
function commonMovableCriteria(name) {
if(typeof(elements[name]) !== "object") {
throw new Error(`Nonexistent element ${name}`);
};
var info = elements[name];
//console.log(`${name} (${JSON.stringify(elements[name])})`);
if(typeof(info.state) === "undefined") {
var state = null;
} else {
var state = info.state;
};
if(typeof(info.category) === "undefined") {
var category = "other";
} else {
var category = info.category;
};
if(excludedFairyElements.includes(name) || excludedCloudElements.includes(name)) {
return false
};
if(elements[name].behavior && elements[name].behavior.toString() == elements.wall.behavior.toString() && !elements[name].tick) {
return false;
};
if(["liquid","gas"].includes(state)) {
return true;
};
if(info.movable) {
return true;
};
if(backupCategoryWhitelist.includes(category)) {
return true;
};
if(backupElementWhitelist.includes(name)) {
return true;
};
if(category.includes("mudstone")) {
return true;
};
return false;
};
//runAfterLoad calls
//Bombs
runAfterLoad(function() {
if(enabledMods.includes("mods/fey_and_more.js")) {
amalgamatedBombFire += ",poisonwater".repeat(8);
amalgamatedBombFire += ",mystic_fire".repeat(4);
amalgamatedBombFire += ",firesea".repeat(6);
amalgamatedBombFire += ",lektre".repeat(6);
};
if(enabledMods.includes("mods/Neutronium Mod.js")) {
amalgamatedBombFire += ",flamer".repeat(3);
amalgamatedBombFire += ",flamebomb".repeat(3);
amalgamatedBombFire += ",toxin".repeat(3);
};
if(enabledMods.includes("mods/randomness.js")) {
amalgamatedBombFire += ",burning_unnamed_gas".repeat(4);
amalgamatedBombFire += ",warp".repeat(6);
amalgamatedBombFire += ",bomb_3".repeat(3);
amalgamatedBombFire += ",op_hottester_bomb".repeat(3);
eLists.BOMB.push("unnamed_bomb");
eLists.BOMB.push("warp_bomb");
};
if(enabledMods.includes("mods/glenn_gases.js")) {
amalgamatedBombFire += ",electric_gas".repeat(3);
amalgamatedBombFire += ",corrosive_gas".repeat(3);
amalgamatedBombFire += ",iocalfaeus_gas".repeat(3);
amalgamatedBombFire += ",ignited_gas".repeat(3);
amalgamatedBombFire += ",finine".repeat(3);
amalgamatedBombFire += ",acidic_vapour".repeat(3);
amalgamatedBombFire += ",nitrous_gas".repeat(3);
amalgamatedBombFire += ",void_gas".repeat(3);
amalgamatedBombFire += ",black_damp".repeat(3);
};
if(enabledMods.includes("mods/some_tf_liquids.js")) {
amalgamatedBombFire += ",blazing_pyrotheum".repeat(5);
amalgamatedBombFire += ",tectonic_petrotheum".repeat(7);
amalgamatedBombFire += ",resonant_ender".repeat(5);
};
if(enabledMods.includes("mods/chem.js")) {
amalgamatedBombFire += ",FOOF".repeat(8);
};
if(enabledMods.includes("mods/the_ground.js")) {
amalgamatedBombFire += ",liquid_irradium".repeat(7);
};
if(enabledMods.includes("mods/bioooze.js")) {
amalgamatedBombFire += ",bioooze".repeat(8);
};
});
//Fairies
runAfterLoad(function() {
if(typeof(eLists.FAIRY) === "undefined") { eLists.FAIRY = [] };
eLists.FAIRY.push("acid_fairy");
eLists.FAIRY.push("oil_fairy");
eLists.FAIRY.push("honey_fairy");
fairyChoices = eLists.FAIRY;
for(i = 0; i < eLists.FAIRY.length; i++) {
var fairyName = eLists.FAIRY[i];
if(!fairyChoices.includes(fairyName)) {
fairyChoices.push(fairyName);
};
};
});
//Non-generated elements
//Bombs
elements.anti_bomb = {
color: "#625c71",
behavior: [
"M2|M1 AND EX:10|M2",
"XX|XX|XX",
"XX|EX:10|XX",
],
category: "weapons",
state: "solid",
density: 1300,
excludeRandom: true,
cooldown: defaultCooldown,
};
elements.firebomb = {
color: "#ee7e3e",
tick: function(pixel) {
if(!isEmpty(pixel.x,pixel.y-1,true)) { //[0][1] EX (ignore bounds)
var newPixel = pixelMap[pixel.x][pixel.y-1];
var newElement = newPixel.element;
var newInfo = elements[newElement];
if(newInfo.state !== "gas" && newElement !== pixel.element) {
explodeAtPlus(pixel.x,pixel.y,10,"fire,fire,fire,fire,fire,greek_fire","fire",null,firebombFire);
};
};
if(!isEmpty(pixel.x,pixel.y+1,true)) { //[2][1] EX (don't ignore bounds, non-bound case)
var newPixel = pixelMap[pixel.x][pixel.y+1];
var newElement = newPixel.element;
var newInfo = elements[newElement];
if(newInfo.state !== "gas" && newElement !== pixel.element) {
explodeAtPlus(pixel.x,pixel.y,10,"fire,fire,fire,fire,fire,greek_fire","fire",null,firebombFire);
};
};
if(outOfBounds(pixel.x,pixel.y+1)) { //[2][1] EX (don't ignore bounds, bound case)
explodeAtPlus(pixel.x,pixel.y,10,"fire,fire,fire,fire,fire,greek_fire","fire",null,firebombFire);
};
if(!tryMove(pixel,pixel.x,pixel.y+1)) { //behaviors.POWDER
Math.random() < 0.5 ? tryMove(pixel,pixel.x-1,pixel.y+1) : tryMove(pixel,pixel.x+1,pixel.y+1);
};
},
category: "weapons",
state: "solid",
density: 1500,
excludeRandom: true,
desc: "An advanced incendiary weapon. <br/>To enable automatic bomb generation, set the generateBombs query parameter.",
};
elements.cluster_nuke = {
color: "#e3f636",
behavior: [
"CR:radiation%5|EX:90>plasma,plasma,plasma,nuke,nuke,nuke,radiation,radiation,radiation,rad_steam,rad_steam,radiation,rad_steam AND CR:radiation%5|CR:radiation%5",
"CR:radiation%5|XX|CR:radiation%5",
"M2 AND CR:radiation%5|M1 AND EX:90>plasma,plasma,plasma,nuke,nuke,nuke,radiation,radiation,radiation,rad_steam,rad_steam,radiation,rad_steam AND CR:radiation%5|M2 AND CR:radiation%5",
],
category: "weapons",
state: "solid",
density: 1500,
excludeRandom: true,
desc: "It's a nuke that drops more nukes. <br/>To enable automatic bomb generation, set the generateBombs query parameter.",
};
elements.anti_bomb = {
color: "#525c61",
behavior: [
"M2|M1 AND EX:10|M2",
"XX|XX|XX",
"XX|EX:10|XX",
],
category: "weapons",
state: "solid",
density: 1300,
excludeRandom: true,
};
elements.electric_bomblet = {
color: "#ffffff",
behavior: [
"SH%50|EX:8>electric AND SH%50|SH%50",
"SH%50|EX:9>electric%0.5|SH%50",
"M2 AND SH%50|M1 AND SH%50 AND EX:8>electric AND SW:electric|M2 AND SH%50",
],
category: "weapons",
state: "solid",
density: 1200,
hidden: true,
excludeRandom: true,
hardness: 0.3,
};
elements.electric_cluster_bomb = {
color: "#ffffff",
behavior: [
"SH%50|EX:8>electric_bomblet AND SH%50|SH%50",
"SH%50|XX|SH%50",
"M2 AND SH%50|M1 AND SH%50 AND EX:8>electric_bomblet AND SW:electric|M2 AND SH%50",
],
category: "weapons",
state: "solid",
density: 1800,
hidden: true,
excludeRandom: true,
hardness: 0.3,
};
elements.radioactive_popper = {
color: "#d6ce72",
behavior: [
"XX|EX:7>radiation|XX",
"XX|XX|XX",
"M2|M1 AND EX:7>radiation|M2",
],
category: "weapons",
state: "solid",
density: 1200,
hidden: true,
excludeRandom: true,
hardness: 0.3,
cooldown: 3,
};
elements.acid_bomb = {
color: "#7d8a63",
behavior: [
"XX|EX:15>acid_gas|XX",
"XX|XX|XX",
"M2|M1 AND EX:15>acid_gas|M2",
],
category: "weapons",
state: "solid",
density: 1400,
excludeRandom: true,
cooldown: defaultCooldown,
};
elements.amalgamated_bomb = {
color: ["#FF0000","#FF0000","#FFFF00","#FFFF00","#00FF00","#00FF00","#0000FF","#0000FF"],
tick: function(pixel) {
doDefaults(pixel);
if(!isEmpty(pixel.x,pixel.y-1,true)) { //[0][1] EX (ignore bounds)
var newPixel = pixelMap[pixel.x][pixel.y-1];
var newElement = newPixel.element;
var newInfo = elements[newElement];
if(newInfo.state !== "gas" && newElement !== pixel.element) {
explodeAtPlus(pixel.x,pixel.y,70,amalgamatedBombFire,amalgamatedBombFire);
};
};
if(!isEmpty(pixel.x,pixel.y+1,true)) { //[2][1] EX (don't ignore bounds, non-bound case)
var newPixel = pixelMap[pixel.x][pixel.y+1];
var newElement = newPixel.element;
var newInfo = elements[newElement];
if(newInfo.state !== "gas" && newElement !== pixel.element) {
explodeAtPlus(pixel.x,pixel.y,70,amalgamatedBombFire,amalgamatedBombFire);
};
};
if(outOfBounds(pixel.x,pixel.y+1)) { //[2][1] EX (don't ignore bounds, bound case)
explodeAtPlus(pixel.x,pixel.y,70,amalgamatedBombFire,amalgamatedBombFire);
};
if(!tryMove(pixel,pixel.x,pixel.y+1)) { //behaviors.POWDER
Math.random() < 0.5 ? tryMove(pixel,pixel.x-1,pixel.y+1) : tryMove(pixel,pixel.x+1,pixel.y+1);
};
},
category: "weapons",
state: "solid",
temp: 7065,
density: 158000,
excludeRandom: true,
};
elements.op_hottester_bomb = {
color: "#cc436e",
properties: {
radius: 15, //just so people can edit it per pixel to be stupidly high
},
tick: function(pixel) {
doDefaults(pixel);
if(!isEmpty(pixel.x,pixel.y-1,true)) { //[0][1] EX (ignore bounds)
var newPixel = pixelMap[pixel.x][pixel.y-1];
var newElement = newPixel.element;
var newInfo = elements[newElement];
if(newInfo.state !== "gas" && newElement !== pixel.element) {
explodeAtPlus(pixel.x,pixel.y,pixel.radius,"plasma","plasma",hotterBomb,hotterBomb,false);
};
};
if(!isEmpty(pixel.x,pixel.y+1,true)) { //[2][1] EX (don't ignore bounds, non-bound case)
var newPixel = pixelMap[pixel.x][pixel.y+1];
var newElement = newPixel.element;
var newInfo = elements[newElement];
if(newInfo.state !== "gas" && newElement !== pixel.element) {
explodeAtPlus(pixel.x,pixel.y,pixel.radius,"plasma","plasma",hotterBomb,hotterBomb,false);
};
};
if(outOfBounds(pixel.x,pixel.y+1)) { //[2][1] EX (don't ignore bounds, bound case)
explodeAtPlus(pixel.x,pixel.y,pixel.radius,"plasma","plasma",hotterBomb,hotterBomb,false);
};
if(!tryMove(pixel,pixel.x,pixel.y+1)) { //behaviors.POWDER
Math.random() < 0.5 ? tryMove(pixel,pixel.x-1,pixel.y+1) : tryMove(pixel,pixel.x+1,pixel.y+1);
};
},
category: "weapons",
state: "solid",
temp: 7065,
density: 1900,
excludeRandom: true,
};
elements.op_electromagneticester_emp = {
color: "#818253",
tick: function(pixel) { //replace EMP
if (pixel.start===pixelTicks) {return}
if (!tryMove(pixel,pixel.x,pixel.y+1)) {
if (outOfBounds(pixel.x,pixel.y+1) || (pixelMap[pixel.x][pixel.y+1].element!=="emp_bomb" && elements[pixelMap[pixel.x][pixel.y+1].element].state!=="gas")) {
for (i = 0; i < currentPixels.length; i++) {
var newPixel = currentPixels[i];
if (newPixel.charge) {
delete newPixel.charge;
newPixel.chargeCD = 50;
}
}
explodeAtPlus(pixel.x,pixel.y+1,20,"electric","smoke",empCharge,null,false);
}
}
doDefaults(pixel);
},
maxSize: 1,
category: "weapons",
state: "solid",
density: 1500,
excludeRandom: true,
alias: "overpowered electromagnetic pulse bomb",
cooldown: defaultCooldown
};
if(enabledMods.includes("mods/the_ground.js")) { //uses things from that but not worth requiring for the whole mod
elements.star_bomb = {
color: "#fffbb5",
properties: {
radius: 50, //just so people can edit it per pixel to be stupidly high
},
tick: function(pixel) {
var starFire = "stellar_plasma,stellar_plasma,stellar_plasma,liquid_stellar_plasma,liquid_stellar_plasma,plasma,plasma";
var starSmoke = "light,light,radiation";
doDefaults(pixel);
if(!isEmpty(pixel.x,pixel.y-1,true)) { //[0][1] EX (ignore bounds)
var newPixel = pixelMap[pixel.x][pixel.y-1];
newPixel.temp += 10000000; //[0][1] HT:10000000
var newElement = newPixel.element;
var newInfo = elements[newElement];
if(newInfo.state !== "gas" && newElement !== pixel.element) {
explodeAtPlus(pixel.x,pixel.y,pixel.radius,starFire,starSmoke,starbombHeat,starbombHeat,false);
};
};
if(!isEmpty(pixel.x,pixel.y+1,true)) { //[2][1] EX (don't ignore bounds, non-bound case)
var newPixel = pixelMap[pixel.x][pixel.y+1];
newPixel.temp += 10000000;
var newElement = newPixel.element;
var newInfo = elements[newElement];
if(newInfo.state !== "gas" && newElement !== pixel.element) {
explodeAtPlus(pixel.x,pixel.y,pixel.radius,starFire,starSmoke,starbombHeat,starbombHeat,false);
};
};
if(outOfBounds(pixel.x,pixel.y+1)) { //[2][1] EX (don't ignore bounds, bound case)
explodeAtPlus(pixel.x,pixel.y,pixel.radius,starFire,starSmoke,starbombHeat,starbombHeat,false);
};
if(!tryMove(pixel,pixel.x,pixel.y+1)) { //behaviors.POWDER
Math.random() < 0.5 ? tryMove(pixel,pixel.x-1,pixel.y+1) : tryMove(pixel,pixel.x+1,pixel.y+1);
};
},
category: "weapons",
state: "solid",
density: 3.663e33,
excludeRandom: true,
cooldown: defaultCooldown
};
};
//Fairies
elements.acid_fairy = {
name: "acid fairy",
color: ["#e2f777","#d1ff94","#d8f7c1"],
behavior: [
"XX|M1|M1",
"XX|FX%5|XX",
"XX|CR:acid%0.5 AND CR:fairy_dust%0.005 AND M1|M1",
],
state: "solid",
category: "fey",
desc: "Like the other fairies, but with acid. <br/>To enable automatic fairy generation, set the generateFairies query parameter.",
}
elements.oil_fairy = {
name: "oil fairy",
color: ["#636360","#a6956f","#a3816d","#cfc191"],
behavior: [
"XX|M1|M1",
"XX|FX%5|XX",
"XX|CR:oil%0.5 AND CR:fairy_dust%0.005 AND M1|M1",
],
state: "solid",
category: "fey",
desc: "Like the other fairies, but with oil. <br/>To enable automatic fairy generation, set the generateFairies query parameter.",
}
elements.honey_fairy = {
name: "honey fairy",
color: ["#ffeaa6","#ffe987","#f2e7c2"],
behavior: [
"XX|M1|M1",
"XX|FX%5|XX",
"XX|CR:honey%0.5 AND CR:fairy_dust%0.005 AND M1|M1",
],
state: "solid",
category: "fey",
desc: "Like the other fairies, but with sweet honey. <br/>To enable automatic fairy generation, set the generateFairies query parameter.",
}
//Sequential generations
//Bombs
for (var i = 2; i <= bombAmount + 1; i++) {
elements[`bomb_${i}`] = {
name: `bomb ${i}`,
color: "#624c41",
behavior: [
`XX|EX:${5*(i+1)}>fire|XX`,
"XX|XX|XX",
`M2|M1 AND EX:${5*(i+1)}>fire|M2`,
],
state: "solid",
density: 1300 * 8**((i-1)/2),
excludeRandom:true,
category: "weapons",
desc: `${5*(i+1)/10} times the radius of the regular bomb`,
cooldown: defaultCooldown,
};
eLists.BOMB.push(`bomb_${i}`);
};
for (var i = 2; i <= bombAmount + 1; i++) {
elements[`anti_bomb_${i}`] = {
color: "#625c71",
behavior: [
`M2|M1 AND EX:${5*(i+1)}>fire|M2`,
"XX|XX|XX",
`XX|EX:${5*(i+1)}>fire|XX`,
],
state: "solid",
density: 1300 * 8**((i-1)/2),
excludeRandom:true,
category: "weapons",
desc: `${5*(i+1)/10} times the radius of the regular anti-bomb`,
cooldown: defaultCooldown,
};
eLists.BOMB.push(`anti_bomb_${i}`);
};
//Fairies
//For statement by charPointer
if(enabledMods.includes("mods/fey_and_more.js")) {
for (var i = 2; i <= fairyAmount + 1; i++) {
elements[`${i}-fairy`] = {
name: `${i}-fairy`,
color: ["#33007a","#8e009f","#09009f"],
behavior: [
"XX|M1|M1",
"XX|FX%5|XX",
`XX|CR:${i-1}-fairy%1 AND CR:fairy_dust%0.005 AND M1|M1`,
],
reactions: {
"glitter": { "elem1": `${i+1}-fairy`, "elem2": null }
},
state: "solid",
excludeRandom:true,
category: "fey",
hidden: true,
}
if (i == fairyAmount) { elements[`${i}-fairy`]["reactions"] = {}; }
if (i == 2) elements[`${i}-fairy`]["behavior"][2] = `XX|CR:fairy%1 AND CR:fairy_dust%0.005 AND M1|M1`;
eLists.FAIRY.push(`${i}-fairy`);
}
//post-gen
if(!elements.rainbow.reactions) { //rainbow promotes fairy
elements.rainbow.reactions = {};
};
elements.rainbow.reactions.fairy = { "elem1": "2-fairy", "elem2": null }
//remove last fairy's reaction
delete elements[`${fairyAmount + 1}-fairy`].reactions
};
//Main generator functions
//Bombs
function generateBomb(bombElements,isAfterScriptLoading=false,bombNumber=1) {//it can be a single element, though
bombNumber = Math.max(0,bombNumber);
//To specify an array bomb, have the array be inside another array.
/*For reasons related to how element colors are loaded, if this function is being run from a JS mod file, isAfterScriptLoading should be false.
Otherwise, you'll get TypeErrors for some reason when trying to place your bomb. If this is being run after the game has loaded (e.g. in the console),
then isAfterScriptLoading should be true or you might also get TypeErrors (this latter case was a bit inconsistent when I tested it, but
the former case wasn't. **isAfterScriptLoading must be false when this function is run from a JS mod file**.*/
if(typeof(bombElements) === "string") { //it should be an array, so string check
//console.log("String detected");
if(bombElements.includes(",")) { //comma-separated string?
//console.log("Splitting string to array");
bombElements = bombElements.split(","); //,SS to array
} else {
//console.log("Wrapping string in array");
bombElements = [bombElements]; //single string to array
};
};
var returns = [];
for(aaf = 0; aaf < bombElements.length; aaf++) {
var elementOfBomb = bombElements[aaf];
//console.log("1",elementOfBomb);
var allElementsHaveColors = ((elementOfBomb instanceof Array ? elementOfBomb : [elementOfBomb]).map(function(name) { return !!(elements[name]?.color)}).reduce(function(a,b) { return a*b }) == 1)
if(!allElementsHaveColors) { continue };
var startColor;
var randomExcl = 0;
//console.log(elementOfBomb);
var bombName;
//console.log("2-1");
if(typeof(elementOfBomb === "string")) { //comma separated string check
if(elementOfBomb.includes(",")) { //if it is
elementOfBomb = elementOfBomb.split(","); //to array
elementOfBomb = elementOfBomb.filter(function(e) { //strip nonexistent elements
//console.log("3 a");
return typeof(elements[e]) === "object";
});
};
};
if(Array.isArray(elementOfBomb)) {
bombName = `${elementOfBomb.join("_")}_bomb`; //auto placer element name
//array case color concatenator (bombs are always excludeRandom)
startColor = [];
//console.log(elementOfBomb);
for(ll = 0; ll < elementOfBomb.length; ll++) {
if(typeof(elements[elementOfBomb[ll]].excludeRandom !== "undefined")) { //if excludeRandom exists (prevent TypeError)
if(elements[elementOfBomb[ll]].excludeRandom) { //it it's true
randomExcl = 1; //the whole array bomb is excluded (from spawnRandomBomb)
//console.log("array nyet" + elementOfBomb);
};
};
startColor = startColor.concat(elements[elementOfBomb[ll]].color);
};
} else { //they should all be strings, so here
bombName = `${elementOfBomb}_bomb`; //auto placer element name
startColor = elements[elementOfBomb].color;
if(typeof(elements[elementOfBomb].excludeRandom !== "undefined")) { //if excludeRandom exists (prevent TypeError)
if(elements[elementOfBomb].excludeRandom) { //it it's true
//console.log("nyet " + elementOfBomb);
randomExcl = 1; //the bomb is excluded
} else {
//console.log("allow " + elementOfBomb);
randomExcl = 0;
};
};
};
//console.log("e",bombName);
//Color gen
if(Array.isArray(startColor)) { //Average arrays, make colors rgb()
startColor = averageRgbPrefixedColorArray(startColor);
} else {
startColor = rgbHexCatcher(startColor);
};
startColor = addColors(changeLuminance(changeSaturation(startColor,0.6,"multiply","hsl_json"),0.5,"multiply","rgb"),"rgb(24,0,0)","rgb");
var newColorObject = rgbStringToObject(startColor);
//End color gen
//The bomb
//console.log(elementOfBomb);
var firstInfo, firstTemp;
if(Array.isArray(elementOfBomb)) {
firstInfo = elements[elementOfBomb[0]];
firstTemp = airTemp;
if(typeof(firstInfo.temp) !== "undefined") {
firstTemp = firstInfo.temp;
};
} else {
firstInfo = elements[elementOfBomb];
firstTemp = airTemp;
if(typeof(firstInfo.temp) !== "undefined") {
firstTemp = firstInfo.temp;
};
};
elementOfBomb = tryJoin(elementOfBomb,",");
descElement = tryJoin(elementOfBomb,", ");
//console.log(elementOfBomb);
if(bombNumber !== 1) {
bombName += `_${bombNumber}`;
};
if(!elementExists(bombName)) {
elements[bombName] = {
color: startColor,
insulate: true,
flippableX: true,
colorObject: newColorObject,
behavior: [
["XX",`EX:${5*(bombNumber+1)}>${elementOfBomb}`,"XX"],
["XX","XX","XX"],
["M2",`M1 AND EX:${5*(bombNumber+1)}>${elementOfBomb}`,"M2"]
],
category: "auto_bombs",
desc: `Explodes into ${descElement}<br/>Radius: ${5*(bombNumber+1)}`,
temp: firstTemp,
excludeRandom: true,
autoType: "bomb",
};
if(typeof(eLists) === "undefined") {
eLists = {};
};
if(typeof(eLists.BOMB) === "undefined") {
eLists.BOMB = [];
};
eLists.BOMB.push(bombName);
if(!randomExcl) {
if(typeof(bombChoices) === "undefined") {
bombChoices = []
};
if(!bombChoices.includes(bombName)) {
bombChoices.push(bombName);
};
}
if(isAfterScriptLoading) {
elements[bombName].flippableX = true;
elementCount++; //increment for new bomb element
createElementButton(bombName);
elements[bombName].id = nextid++;
document.getElementById("extraInfo").innerHTML = "<small><p>There are " + elementCount + " elements, including " + hiddenCount + " hidden ones.</p><p>©2021-" + new Date().getFullYear() + ". All Rights Reserved. <a href='https://r74n.com'>R74n</a></p></small>"; //update extra info counts (and the copyright year, due to the method used)
};
};
returns.push(bombName);
};
return returns;
};
//Clouds
//Clouds
function generateCloud(cloudElements,cloudType=0,isAfterScriptLoading=false) {//it can be a single element, though
//To specify an array cloud, have the array be inside another array.
/*For reasons related to how element colors are loaded, if this function is being run from a JS mod file, isAfterScriptLoading should be false.
Otherwise, you'll get TypeErrors for some reason when trying to place your cloud. If this is being run after the game has loaded (e.g. in the console),
then isAfterScriptLoading should be true or you might also get TypeErrors (this latter case was a bit inconsistent when I tested it, but
the former case wasn't. **isAfterScriptLoading must be false when this function is run from a JS mod file**.*/
if(cloudType !== "*") {
if(typeof(cloudElements) === "string") { //it should be an array, so string check
//console.log("String detected");
if(cloudElements.includes(",")) { //comma-separated string?
//console.log("Splitting string to array");
cloudElements = cloudElements.split(","); //,SS to array
} else {
//console.log("Wrapping string in array");
cloudElements = [cloudElements]; //single string to array
};
};
var returns = [];
for(aaf = 0; aaf < cloudElements.length; aaf++) {
var elementOfCloud = cloudElements[aaf];
var allElementsHaveColors = ((elementOfCloud instanceof Array ? elementOfCloud : [elementOfCloud]).map(function(name) { return !!(elements[name]?.color)}).reduce(function(a,b) { return a*b }) == 1)
if(!allElementsHaveColors) { continue };
var startColor;
var randomExcl = 0;
//console.log("randomExcl set")
//console.log(elementOfCloud);
var cloudName, cloudPrefix, cloudBehavior;
switch(cloudType) {
case 0:
cloudPrefix = "";
cloudBehavior = behaviorGenerators.cloud(elementOfCloud);
break;
case 1:
cloudPrefix = "heavy_";
cloudBehavior = behaviorGenerators.heavy_cloud(elementOfCloud);
break;
case 2:
cloudPrefix = "heavier_";
cloudBehavior = behaviorGenerators.heavier_cloud(elementOfCloud);
break;
case 3:
cloudPrefix = "heaviest_";
cloudBehavior = behaviorGenerators.heaviest_cloud(elementOfCloud);
break;
case 4:
cloudPrefix = "heaviester_";
cloudBehavior = behaviorGenerators.heaviester_cloud(elementOfCloud);
break;
case 5:
cloudPrefix = "heaviestest_";
cloudBehavior = behaviorGenerators.heaviestest_cloud(elementOfCloud);
break;
default:
throw new RangeError("Cloud type must be between 0 and 5");
};
if(typeof(elementOfCloud === "string")) { //comma separated string check
if(elementOfCloud.includes(",")) { //if it is
elementOfCloud = elementOfCloud.split(","); //to array
elementOfCloud = elementOfCloud.filter(function(e) { //strip nonexistent elements
return typeof(elements[e]) === "object";
});
};
};
if(Array.isArray(elementOfCloud)) {
cloudName = `${elementOfCloud.join("_")}_cloud`; //auto placer element name
//array case color concatenator and excludeRandom handler
startColor = [];
//console.log(elementOfCloud);
for(ll = 0; ll < elementOfCloud.length; ll++) {
if(typeof(elements[elementOfCloud[ll]].excludeRandom !== "undefined")) { //if excludeRandom exists (prevent TypeError)
if(elements[elementOfCloud[ll]].excludeRandom) { //it it's true
randomExcl = 1; //the whole array cloud is excluded
//console.log("array nyet" + elementOfCloud);
};
};
//console.log(elementOfCloud[ll]);
startColor = startColor.concat(elements[elementOfCloud[ll]].color);
};
} else { //they should all be strings, so here
cloudName = `${elementOfCloud}_cloud`; //auto placer element name
startColor = elements[elementOfCloud].color;
if(typeof(elements[elementOfCloud].excludeRandom !== "undefined")) { //if excludeRandom exists (prevent TypeError)
if(elements[elementOfCloud].excludeRandom) { //it it's true
//console.log("nyet " + elementOfCloud);
randomExcl = 1; //the cloud is excluded
} else {
//console.log("allow " + elementOfCloud);
randomExcl = 0;
};
};
};
//Color gen
if(Array.isArray(startColor)) { //Average arrays, make colors rgb()
startColor = averageRgbPrefixedColorArray(startColor);
} else {
startColor = rgbHexCatcher(startColor);
};
startColor = changeLuminance(
changeSaturation(
startColor,
(0.4 + (cloudType/10)),
"multiply",
"hsl_json"
),
(0.6 - (cloudType/10)),
"multiply",
"rgb"
);
var newColorObject = rgbStringToObject(startColor);
//End color gen
//The cloud
//console.log(elementOfCloud);
var firstInfo, firstTemp;
if(Array.isArray(elementOfCloud)) {
firstInfo = elements[elementOfCloud[0]];
firstTemp = airTemp;
if(typeof(firstInfo.temp) !== "undefined") {
firstTemp = firstInfo.temp;
};
} else {
firstInfo = elements[elementOfCloud];
firstTemp = airTemp;
if(typeof(firstInfo.temp) !== "undefined") {
firstTemp = firstInfo.temp;
};
};
elementOfCloud = tryJoin(elementOfCloud,",");
//console.log(elementOfCloud);
cloudName = cloudPrefix + cloudName;
if(elementExists(cloudName)) {
cloudName = "auto_" + cloudName;
};
elements[cloudName] = {
color: startColor,
cloudType: elementOfCloud,
insulate: true,
colorObject: newColorObject,
behavior: cloudBehavior,
category: "clouds",
temp: firstTemp,
state: "gas",
density: 0.6 * (2**cloudType),
ignoreAir: true,
conduct: 0.01 * (2**cloudType),
autoType: "cloud",
};
if(cloudType === 4) { //column tick for heaviester clouds
elements[cloudName].tick = function(pixel) {
singleColumn(pixel,elements[pixel.element].cloudType,0.01,0.3);
};
};
if(cloudType === 5) { //three-column tick for heaviestest clouds
elements[cloudName].tick = function(pixel) {
multiColumn(pixel,elements[pixel.element].cloudType,0.02,0.4,1);
};
};
eLists.CLOUD.push(cloudName);
if(!randomExcl) {
if(typeof(cloudChoices) === "undefined") {
cloudChoices = []
};
if(!cloudChoices.includes(cloudName)) {
cloudChoices.push(cloudName);
};
}
if(cloudIncludeRandom) {
randomExcl ? elements[cloudName].excludeRandom = true : elements[cloudName].excludeRandom = false;
} else {
elements[cloudName].excludeRandom = true;
};
if(isAfterScriptLoading) {
elementCount++; //increment for new cloud element
createElementButton(cloudName);
elements[cloudName].id = nextid++;
document.getElementById("extraInfo").innerHTML = "<small><p>There are " + elementCount + " elements, including " + hiddenCount + " hidden ones.</p><p>©2021-" + new Date().getFullYear() + ". All Rights Reserved. <a href='https://r74n.com'>R74n</a></p></small>"; //update extra info counts (and the copyright year, due to the method used)
};
returns.push(cloudName);
};
return returns;
} else if(cloudType === "*") {
var trueReturns = [];
for(cloudTypeForIndex = 0; cloudTypeForIndex <= 5; cloudTypeForIndex++) {
trueReturns = trueReturns.concat(generateCloud(cloudElements,cloudTypeForIndex,isAfterScriptLoading));
};
return trueReturns;
};
};
//Creepers
function generateCreeper(creeperElements,isAfterScriptLoading=false) {//it can be a single element, though
//To specify an array creeper, have the array be inside another array.
/*For reasons related to how element colors are loaded, if this function is being run from a JS mod file, isAfterScriptLoading should be false.
Otherwise, you'll get TypeErrors for some reason when trying to place your creeper. If this is being run after the game has loaded (e.g. in the console),
then isAfterScriptLoading should be true or you might also get TypeErrors (this latter case was a bit inconsistent when I tested it, but
the former case wasn't. **isAfterScriptLoading must be false when this function is run from a JS mod file**.
If isAfterScriptLoading is true, buttons (depending on the hiding setting) will be added to the auto creeper category, the 3 new elements per creeper will be assigned IDs, and the footer will be updated with the increased element counts.*/
if(typeof(creeperElements) === "string") { //it should be an array, so string check
//console.log("String detected");
if(creeperElements.includes(",")) { //comma-separated string?
//console.log("Splitting string to array");
creeperElements = creeperElements.split(","); //,SS to array
} else {
//console.log("Wrapping string in array");
creeperElements = [creeperElements]; //single string to array
};
};
var returns = [];
for(aaf = 0; aaf < creeperElements.length; aaf++) {
var elementOfCreeper = creeperElements[aaf];
var allElementsHaveColors = ((elementOfCreeper instanceof Array ? elementOfCreeper : [elementOfCreeper]).map(function(name) { return !!(elements[name]?.color)}).reduce(function(a,b) { return a*b }) == 1)
if(!allElementsHaveColors) { continue };
var startColor;
var randomExcl = 0;
//console.log("randomExcl set")
//console.log(elementOfCreeper);
var headName,bodyName,placerName,descElement;
if(typeof(elementOfCreeper === "string")) { //comma separated string check
if(elementOfCreeper.includes(",")) { //if it is
elementOfCreeper = elementOfCreeper.split(","); //to array
elementOfCreeper = elementOfCreeper.filter(function(e) { //strip nonexistent elements
return typeof(elements[e]) === "object";
});
};
};
if(Array.isArray(elementOfCreeper)) {
headName = `${elementOfCreeper.join("_")}_creeper_head`; //auto head element name
bodyName = `${elementOfCreeper.join("_")}_creeper_body`; //auto body element name
placerName = `${elementOfCreeper.join("_")}_creeper`; //auto placer element name
descElement = elementOfCreeper.join(", "); //auto explosion element list
//array case color concatenator and excludeRandom handler
startColor = [];
//console.log(elementOfCreeper);
for(ll = 0; ll < elementOfCreeper.length; ll++) {
if(typeof(elements[elementOfCreeper[ll]].excludeRandom !== "undefined")) { //if excludeRandom exists (prevent TypeError)
if(elements[elementOfCreeper[ll]].excludeRandom) { //it it's true
randomExcl = 1; //the whole array creeper is excluded
//console.log("array nyet" + elementOfCreeper);
};
};
//console.log(elementOfCreeper[ll]);
startColor = startColor.concat(elements[elementOfCreeper[ll]].color);
};
} else { //they should all be strings, so here
headName = `${elementOfCreeper}_creeper_head`; //auto head element name
bodyName = `${elementOfCreeper}_creeper_body`; //auto body element name
placerName = `${elementOfCreeper}_creeper`; //auto placer element name
descElement = elementOfCreeper; //auto explosion element
startColor = elements[elementOfCreeper].color;
if(typeof(elements[elementOfCreeper].excludeRandom !== "undefined")) { //if excludeRandom exists (prevent TypeError)
if(elements[elementOfCreeper].excludeRandom) { //it it's true
//console.log("nyet " + elementOfCreeper);
randomExcl = 1; //the creeper is excluded
} else {
//console.log("allow " + elementOfCreeper);
randomExcl = 0;
};
};
};
//Color gen
if(Array.isArray(startColor)) { //Average arrays, make colors rgb()
startColor = averageRgbPrefixedColorArray(startColor);
} else {
startColor = rgbHexCatcher(startColor);
};
var preColor = rgbStringToHSL(startColor);
var colorsArray = [preColor, preColor, preColor, preColor, preColor, preColor, preColor, preColor, preColor, preColor]
var colorObjectArray = [];
for(q = 0; q < hslOffsets.length; q++) {
colorsArray[q] = addArraysInPairs(colorsArray[q],hslOffsets[q]);
colorsArray[q] = hslToHex(...colorsArray[q]);
colorObjectArray[q] = hexToRGB(colorsArray[q]); //outputs hex
if(isAfterScriptLoading) { // if it's after the hex -> RGB conversion
var coq = colorObjectArray[q]; //pull the object
//console.log(coq.r);
colorsArray[q] = `rgb(${coq.r},${coq.g},${coq.b})`; //and change to the RGB from its values
};
};
//End color gen
//console.log(`${headName}; ${bodyName}; ${placerName}; ${descElement}`)
//Placer
elements[placerName] = {
movable: true,
creeperType: elementOfCreeper,
color: colorsArray,
colorObject: colorObjectArray,
category: "auto creepers",
properties: {
dead: false,
dir: 1,
panic: 0,
following: false,
},
tick: function(pixel) {
autoCreeperPlacerTick(pixel);
},
related: [bodyName,headName,"creeper"],
desc: `Auto-generated creeper.<br/>Explodes into ${descElement}.`,
autoType: "creeper",
};
eLists.CREEPER.push(placerName);
//Body
elements[bodyName] = {
movable: true,
creeperType: elementOfCreeper,
color: colorsArray,
colorObject: colorObjectArray,
category: "auto creepers",
hidden: true,
excludeRandom: true,
density: 1500,
state: "solid",
conduct: 25,
tempHigh: 250,
stateHigh: "cooked_meat",
tempLow: -30,
stateLow: "frozen_meat",
burn: 10,
burnTime: 250,
burnInto: ["cooked_meat","cooked_meat","cooked_meat","cooked_meat","gunpowder"],
breakInto: ["blood","gunpowder"],
reactions: {
"cancer": { "elem1":"cancer", "chance":0.005 },
"radiation": { "elem1":["ash","meat","rotten_meat","cooked_meat"], "chance":0.4 },
"plague": { "elem1":"plague", "chance":0.05 },
},
properties: {
dead: false,
dir: 1,
panic: 0,
charged: false,
didChargeBlueTinted: false,
},
tick: function(pixel) {
autoCreeperBodyTick(pixel);
},
autoType: "creeper",
};
//Head
elements[headName] = {
movable: true,
creeperType: elementOfCreeper,
color: colorsArray,
colorObject: colorObjectArray,
category: "auto creepers",
hidden: true,
excludeRandom: true,
density: 1080,
state: "solid",
conduct: 25,
tempHigh: 250,
stateHigh: "cooked_meat",
tempLow: -30,
stateLow: "frozen_meat",
burn: 10,
burnTime: 250,
burnInto: ["cooked_meat","cooked_meat","cooked_meat","cooked_meat","cooked_meat","cooked_meat","cooked_meat","cooked_meat","cooked_meat","gunpowder"],
breakInto: "blood",
reactions: {
"cancer": { "elem1":"cancer", "chance":0.005 },
"radiation": { "elem1":["ash","meat","rotten_meat","cooked_meat"], "chance":0.4 },
"plague": { "elem1":"plague", "chance":0.05 },
"oxygen": { "elem2":"carbon_dioxide", "chance":0.5 },
},
properties: {
dead: false,
following: false,
hissing: false,
charged: false,
didChargeBlueTinted: false,
},
tick: function(pixel) {
autoCreeperHeadTick(pixel);
},
autoType: "creeper",
};
if(isAfterScriptLoading) {
elementCount += 3; //placer, body, head
createElementButton(placerName)
if(settings["unhide"] === 0) { //hide some elements: body and head would be hidden, so only update hidden count
hiddenCount += 2;
} else if(settings["unhide"] === 1) { //unhide all elements: b/h would not be hidden, so only create their buttons
createElementButton(bodyName);
createElementButton(headName);
} else if(settings["unhide"] === 2) {
settings.unlocked[bodyName] ? createElementButton(bodyName) : hiddenCount++; //ternary: if headName is unlocked, create button, else increase hiddenCount
settings.unlocked[headName] ? createElementButton(headName) : hiddenCount++; //above with headName
};
elements[placerName].id = nextid++; //set placer's id to nextid and then increment nextid
elements[bodyName].id = nextid++; //repeat with body and head
elements[headName].id = nextid++;
headBodyObject[headName] = bodyName;
document.getElementById("extraInfo").innerHTML = "<small><p>There are " + elementCount + " elements, including " + hiddenCount + " hidden ones.</p><p>©2021-" + new Date().getFullYear() + ". All Rights Reserved. <a href='https://r74n.com'>R74n</a></p></small>"; //update extra info counts (and the copyright year, due to the method used)
};
if(creeperIncludeRandom) {
randomExcl ? elements[placerName].excludeRandom = true : elements[placerName].excludeRandom = false;
} else {
elements[placerName].excludeRandom = true;
};
if(!randomExcl) {
//console.log("spawn enabling " + placerName);
spawnCreepers.push(placerName);
} else {
//console.log("nyetted " + placerName);
};
returns.push(placerName);
};
return returns;
};
//Fairies
function generateFairy(fairyElements,isAfterScriptLoading=false) {//it can be a single element, though
//To specify an array fairy, have the array be inside another array.
/*For reasons related to how element colors are loaded, if this function is being run from a JS mod file, isAfterScriptLoading should be false.
Otherwise, you'll get TypeErrors for some reason when trying to place your fairy. If this is being run after the game has loaded (e.g. in the console),
then isAfterScriptLoading should be true or you might also get TypeErrors (this latter case was a bit inconsistent when I tested it, but
the former case wasn't. **isAfterScriptLoading must be false when this function is run from a JS mod file**.*/
if(typeof(fairyElements) === "string") { //it should be an array, so string check
//console.log("String detected");
if(fairyElements.includes(",")) { //comma-separated string?
//console.log("Splitting string to array");
fairyElements = fairyElements.split(","); //,SS to array
} else {
//console.log("Wrapping string in array");
fairyElements = [fairyElements]; //single string to array
};
};
var returns = [];
for(aaf = 0; aaf < fairyElements.length; aaf++) {
var elementOfFairy = fairyElements[aaf];
var allElementsHaveColors = ((elementOfFairy instanceof Array ? elementOfFairy : [elementOfFairy]).map(function(name) { return !!(elements[name]?.color)}).reduce(function(a,b) { return a*b }) == 1)
if(!allElementsHaveColors) { continue };
var startColor;
var randomExcl = 0;
var isNocheer = 0;
//console.log("randomExcl set")
//console.log(elementOfFairy);
var fairyName;
if(typeof(elementOfFairy === "string")) { //comma separated string check
if(elementOfFairy.includes(",")) { //if it is
elementOfFairy = elementOfFairy.split(","); //to array
elementOfFairy = elementOfFairy.filter(function(e) { //strip nonexistent elements
return typeof(elements[e]) === "object";
});
};
};
if(Array.isArray(elementOfFairy)) {
fairyName = `${elementOfFairy.join("_")}_fairy`; //auto placer element name
//array case color concatenator and excludeRandom handler
startColor = [];
//console.log(elementOfFairy);
for(ll = 0; ll < elementOfFairy.length; ll++) {
if(typeof(elements[elementOfFairy[ll]].excludeRandom !== "undefined")) { //if excludeRandom exists (prevent TypeError)
if(elements[elementOfFairy[ll]].excludeRandom) { //it it's true
randomExcl = 1; //the whole array fairy is excluded
//console.log("array nyet" + elementOfFairy);
};
};
//console.log(elementOfFairy[ll]);
startColor = startColor.concat(elements[elementOfFairy[ll]].color);
};
for(ll = 0; ll < elementOfFairy.length; ll++) {
if(typeof(elements[elementOfFairy[ll]].nocheer !== "undefined")) { //if nocheer exists (prevent TypeError)
if(elements[elementOfFairy[ll]].nocheer) { //it it's true
isNocheer = 1; //the whole array fairy is excluded
//console.log("array nyet" + elementOfFairy);
};
};
//console.log(elementOfFairy[ll]);
startColor = startColor.concat(elements[elementOfFairy[ll]].color);
};
} else { //they should all be strings, so here
fairyName = `${elementOfFairy}_fairy`; //auto placer element name
startColor = elements[elementOfFairy].color;
if(typeof(elements[elementOfFairy].excludeRandom !== "undefined")) { //if excludeRandom exists (prevent TypeError)
if(elements[elementOfFairy].excludeRandom) { //it it's true
//console.log("nyet " + elementOfFairy);
randomExcl = 1; //the fairy is excluded
} else {
//console.log("allow " + elementOfFairy);
randomExcl = 0;
};
};
if(typeof(elements[elementOfFairy].nocheer !== "undefined")) { //if nocheer exists (prevent TypeError)
if(elements[elementOfFairy].nocheer) { //it it's true
//console.log("nyet " + elementOfFairy);
isNocheer = 1; //the fairy is excluded
} else {
//console.log("allow " + elementOfFairy);
isNocheer = 0;
};
};
};
//Color gen
if(Array.isArray(startColor)) { //Average arrays, make colors rgb()
startColor = averageRgbPrefixedColorArray(startColor);
} else {
startColor = rgbHexCatcher(startColor);
};
//console.log(`rgbStringToObject(${startColor}) from more_fairies.js`)
var newColorObjectArray = [];
var newColorArray = [];
for(i = 0; i < elements.fairy.color.length; i++) {
var newFairyColorlet = elements.fairy.color[i];
var newColor = multiplyColors(startColor,newFairyColorlet);
var newColorJSON = multiplyColors(startColor,newFairyColorlet,"json");
newColorArray.push(newColor);
newColorObjectArray.push(newColorJSON);
};
//End color gen
//The fairy
//console.log(elementOfFairy);
var firstInfo, firstTemp;
if(Array.isArray(elementOfFairy)) {
firstInfo = elements[elementOfFairy[0]];
firstTemp = airTemp;
if(typeof(firstInfo.temp) !== "undefined") {
firstTemp = firstInfo.temp;
};
} else {
firstInfo = elements[elementOfFairy];
firstTemp = airTemp;
if(typeof(firstInfo.temp) !== "undefined") {
firstTemp = firstInfo.temp;
};
};
elementOfFairy = tryJoin(elementOfFairy,",");
//console.log(elementOfFairy);
if(!elementExists(fairyName)) {
elements[fairyName] = {
color: newColorArray,
insulate: true,
flippableX: true,
colorObject: newColorObjectArray,
behavior: [
["XX","M1","M1"],
["XX","FX%5","XX"],
["XX",`CR:${elementOfFairy}%0.5 AND CR:fairy_dust%0.005 AND M1`,"M1"]
],
category: "auto_fey",
temp: firstTemp,
hardness: 1,
autoType: "fairy",
};
if(typeof(eLists) === "undefined") {
eLists = {};
};
if(typeof(eLists.FAIRY) === "undefined") {
eLists.FAIRY = [];
};
eLists.FAIRY.push(fairyName);
if(!randomExcl) {
if(typeof(fairyChoices) === "undefined") {
fairyChoices = []
};
if(!fairyChoices.includes(fairyName)) {
fairyChoices.push(fairyName);
};
}
if(isNocheer) {
elements[fairyName].nocheer = true;
}
if(fairyIncludeRandom) {
randomExcl ? elements[fairyName].excludeRandom = true : elements[fairyName].excludeRandom = false;
} else {
elements[fairyName].excludeRandom = true;
};
if(isAfterScriptLoading) {
elements[fairyName].flippableX = true;
elementCount++; //increment for new fairy element
if (settings.cheerful && elements[fairyName].nocheer) {
elements[fairyName].hidden = true;
hiddenCount++;
} else {
createElementButton(fairyName);
};
elements[fairyName].id = nextid++;
document.getElementById("extraInfo").innerHTML = "<small><p>There are " + elementCount + " elements, including " + hiddenCount + " hidden ones.</p><p>©2021-" + new Date().getFullYear() + ". All Rights Reserved. <a href='https://r74n.com'>R74n</a></p></small>"; //update extra info counts (and the copyright year, due to the method used)
};
};
returns.push(fairyName);
};
return returns;
};
//Spouts
function generateSpout(spoutElements,isAfterScriptLoading=false) {//it can be a single element, though
//To specify an array spout, have the array be inside another array.
/*For reasons related to how element colors are loaded, if this function is being run from a JS mod file, isAfterScriptLoading should be false.
Otherwise, you'll get TypeErrors for some reason when trying to place your spout. If this is being run after the game has loaded (e.g. in the console),
then isAfterScriptLoading should be true or you might also get TypeErrors (this latter case was a bit inconsistent when I tested it, but
the former case wasn't. **isAfterScriptLoading must be false when this function is run from a JS mod file**.*/
if(typeof(spoutElements) === "string") { //it should be an array, so string check
//console.log("String detected");
if(spoutElements.includes(",")) { //comma-separated string?
//console.log("Splitting string to array");
spoutElements = spoutElements.split(","); //,SS to array
} else {
//console.log("Wrapping string in array");
spoutElements = [spoutElements]; //single string to array
};
};
var returns = [];
for(aaf = 0; aaf < spoutElements.length; aaf++) {
var elementOfSpout = spoutElements[aaf];
var allElementsHaveColors = ((elementOfSpout instanceof Array ? elementOfSpout : [elementOfSpout]).map(function(name) { return !!(elements[name]?.color)}).reduce(function(a,b) { return a*b }) == 1)
if(!allElementsHaveColors) { continue };
var startColor;
var randomExcl = 0;
var isNocheer = 0;
//console.log("randomExcl set")
//console.log(elementOfSpout);
var spoutName;
if(typeof(elementOfSpout === "string")) { //comma separated string check
if(elementOfSpout.includes(",")) { //if it is
elementOfSpout = elementOfSpout.split(","); //to array
elementOfSpout = elementOfSpout.filter(function(e) { //strip nonexistent elements
return typeof(elements[e]) === "object";
});
};
};
if(Array.isArray(elementOfSpout)) {
spoutName = `${elementOfSpout.join("_")}_spout`; //auto placer element name
//array case color concatenator and excludeRandom handler
startColor = [];
//console.log(elementOfSpout);
for(ll = 0; ll < elementOfSpout.length; ll++) {
if(typeof(elements[elementOfSpout[ll]].excludeRandom !== "undefined")) { //if excludeRandom exists (prevent TypeError)
if(elements[elementOfSpout[ll]].excludeRandom) { //it it's true
randomExcl = 1; //the whole array spout is excluded
//console.log("array nyet" + elementOfSpout);
};
};
//console.log(elementOfSpout[ll]);
startColor = startColor.concat(elements[elementOfSpout[ll]].color);
};
for(ll = 0; ll < elementOfSpout.length; ll++) {
if(typeof(elements[elementOfSpout[ll]].nocheer !== "undefined")) { //if nocheer exists (prevent TypeError)
if(elements[elementOfSpout[ll]].nocheer) { //it it's true
isNocheer = 1; //the whole array spout is excluded
//console.log("array nyet" + elementOfSpout);
};
};
//console.log(elementOfSpout[ll]);
startColor = startColor.concat(elements[elementOfSpout[ll]].color);
};
} else { //they should all be strings, so here
spoutName = `${elementOfSpout}_spout`; //auto placer element name
startColor = elements[elementOfSpout].color;
if(typeof(elements[elementOfSpout].excludeRandom !== "undefined")) { //if excludeRandom exists (prevent TypeError)
if(elements[elementOfSpout].excludeRandom) { //it it's true
//console.log("nyet " + elementOfSpout);
randomExcl = 1; //the spout is excluded
} else {
//console.log("allow " + elementOfSpout);
randomExcl = 0;
};
};
if(typeof(elements[elementOfSpout].nocheer !== "undefined")) { //if nocheer exists (prevent TypeError)
if(elements[elementOfSpout].nocheer) { //it it's true
//console.log("nyet " + elementOfSpout);
isNocheer = 1; //the spout is excluded
} else {
//console.log("allow " + elementOfSpout);
isNocheer = 0;
};
};
};
//Color gen
if(Array.isArray(startColor)) { //Average arrays, make colors rgb()
startColor = averageRgbPrefixedColorArray(startColor);
} else {
startColor = rgbHexCatcher(startColor);
};
var newColorObject = rgbStringToObject(startColor);
//End color gen
//The spout
//console.log(elementOfSpout);
var firstInfo, firstTemp;
if(Array.isArray(elementOfSpout)) {
firstInfo = elements[elementOfSpout[0]];
firstTemp = airTemp;
if(typeof(firstInfo.temp) !== "undefined") {
firstTemp = firstInfo.temp;
};
} else {
firstInfo = elements[elementOfSpout];
firstTemp = airTemp;
if(typeof(firstInfo.temp) !== "undefined") {
firstTemp = firstInfo.temp;
};
};
elementOfSpout = tryJoin(elementOfSpout,",");
//console.log(elementOfSpout);
elements[spoutName] = {
color: startColor,
insulate: true,
colorObject: newColorObject,
behavior: [
["XX",`CR:${elementOfSpout}`,"XX"],
[`CR:${elementOfSpout}`,"XX",`CR:${elementOfSpout}`],
["XX",`CR:${elementOfSpout}`,"XX"]
],
category: "spouts",
temp: firstTemp,
hardness: 1,
autoType: "spout",
};
if(!randomExcl) {
if(typeof(spoutChoices) === "undefined") {
spoutChoices = []
};
if(!spoutChoices.includes(spoutName)) {
spoutChoices.push(spoutName);
};
}
if(spoutIncludeRandom) {
randomExcl ? elements[spoutName].excludeRandom = true : elements[spoutName].excludeRandom = false;
} else {
elements[spoutName].excludeRandom = true;
};
if(isNocheer) {
elements[spoutName].nocheer = true;
}
if(isAfterScriptLoading) {
elementCount++; //increment for new spout element
if (settings.cheerful && elements[spoutName].nocheer) {
elements[spoutName].hidden = true;
hiddenCount++;
} else {
createElementButton(spoutName);
};
elements[spoutName].id = nextid++;
document.getElementById("extraInfo").innerHTML = "<small><p>There are " + elementCount + " elements, including " + hiddenCount + " hidden ones.</p><p>©2021-" + new Date().getFullYear() + ". All Rights Reserved. <a href='https://r74n.com'>R74n</a></p></small>"; //update extra info counts (and the copyright year, due to the method used)
};
eLists.SPOUT.push(spoutName);
returns.push(spoutName);
};
return returns;
};
//Other runAfterAutogen
//Bombs
runAfterAutogen(function() {
if(elementExists("vaporized_rock")) {
elements.molten_dirt.tempHigh = 3000;
elements.molten_dirt.stateHigh = "vaporized_rock";
};
});
//Modless orphaned code that I don't want meeting La Maison
runAfterAutogen(function() {
var solidBlacklist = ["mistake", "birthpool", "firesea"]; //exclude these since they seem to be liquid
solids = Object.keys(elements).filter(function(e) {
return elements[e].category === "solids" && !solidBlacklist.includes(e);
});
for(i = 0; i < solids.length; i++) { //A lot of elements in solids, particularly metals, are missing a "state: solid".
var solidName = solids[i]
elements[solidName].state = "solid";
};
});
//Mass generation calls
//Bombs
runAfterAutogen(function() {
if(generateBombs) {
var tempArray = Object.keys(elements);
tempArray.push(["rock", "sand"]);
generateBomb(tempArray,false)
};
});
//Creepers
runAfterAutogen(function() {
if(generateCreepers) {
var tempArray = Object.keys(elements);
tempArray.push(["rock", "sand"]);
generateCreeper(tempArray,false)
};
});
//Several
runAfterAutogen(function() {
cloudElements = [...Object.keys(elements).filter(function(e) { //same criteria as spouts
return (commonMovableCriteria(e,excludedCloudElements));
}),["rock","sand"]];
fairyElements = [...Object.keys(elements).filter(function(e) { //same criteria as spouts
return (commonMovableCriteria(e,excludedFairyElements));
}),["rock","sand"]];
spoutElements = [...Object.keys(elements).filter(function(e) { //same criteria as spouts
return (commonMovableCriteria(e,excludedSpoutElements));
}),["rock","sand"]];
if(generateFairies) {
generateFairy(fairyElements,false);
};
if(generateClouds) {
generateCloud(cloudElements,"*",false);
};
if(generateSpouts) {
generateSpout(spoutElements,false);
};
});
//Random spawners
//Bombs
elements.spawn_random_bomb = {
color: ["#141c23","#340f3c","#4b2d3f","#35463f","#244633","#460c1b","#294725","#34290c"],
behavior: behaviors.WALL,
category: "special",
excludeRandom: true,
tick: function(pixel) {
if(bombChoices == undefined || bombChoices.length == 0) {
deletePixel(pixel.x,pixel.y);
return false;
};
changePixel(pixel,bombChoices[Math.floor(Math.random() * bombChoices.length)])
},
};
//Clouds
elements.spawn_random_cloud = {
color: ["#3e5f8a","#a334ec","#ea96f9","#a6ecf6","#70ebc8","#d9286b","#7eed91","#a18b30"],
behavior: behaviors.WALL,
category: "special",
excludeRandom: true,
tick: function(pixel) {
if(cloudChoices == undefined || cloudChoices.length == 0) {
deletePixel(pixel.x,pixel.y);
return false;
};
changePixel(pixel,cloudChoices[Math.floor(Math.random() * cloudChoices.length)])
},
};
//Creepers
elements.spawn_random_creeper = {
color: colorOfRandomCreeper,
behavior: behaviors.WALL,
category: "special",
excludeRandom: false, //see below
movable: true,
tick: function(pixel) {
if(spawnCreepers == undefined || spawnCreepers.length == 0) {
deletePixel(pixel.x,pixel.y);
return false;
};
changePixel(pixel,spawnCreepers[Math.floor(Math.random() * spawnCreepers.length)]) //spawnCreepers is already excludeRandom filtered
},
};
//Fairies
elements.spawn_random_fairy = {
color: ["#3e5f8a","#a334ec","#ea96f9","#a6ecf6","#70ebc8","#d9286b","#7eed91","#a18b30"],
behavior: behaviors.WALL,
category: "special",
excludeRandom: true,
tick: function(pixel) {
if(fairyChoices == undefined || fairyChoices.length == 0) {
deletePixel(pixel.x,pixel.y);
return false;
};
changePixel(pixel,fairyChoices[Math.floor(Math.random() * fairyChoices.length)])
},
};
//Spouts
elements.spawn_random_spout = {
color: ["#3e5f8a","#a334ec","#ea96f9","#a6ecf6","#70ebc8","#d9286b","#7eed91","#a18b30"],
behavior: behaviors.WALL,
category: "special",
excludeRandom: true,
tick: function(pixel) {
if(spoutChoices == undefined || spoutChoices.length == 0) {
deletePixel(pixel.x,pixel.y);
return false;
};
changePixel(pixel,spoutChoices[Math.floor(Math.random() * spoutChoices.length)])
},
};
//Other post-generation
//Fairies
//FAIRYKILL
behaviors.FAIRYKILL_OLD = behaviors.FAIRYKILL;
behaviors.FAIRYKILL = function(pixel) {
if (pixel.start === pixelTicks) {return}
if (pixel.charge && elements[pixel.element].behaviorOn) {
pixelTick(pixel)
}
var ignore = (elements[pixel.element].ignore == "undefined" ? [] : elements[pixel.element].ignore);
for(i = 0; i < adjacentCoords.length; i++) {
var coord = adjacentCoords[i];
var offsetX = coord[0];
var offsetY = coord[1];
var newX = pixel.x+offsetX;
var newY = pixel.y+offsetY;
if(!isEmpty(newX,newY,true)) {
var newPixel = pixelMap[newX][newY];
var newElement = newPixel.element;
var isIgnored = (newElement === ignore || ignore.includes(newElement))
if(eLists.FAIRY.includes(newElement) && !isIgnored) {
deletePixel(newX,newY);
};
};
};
doDefaults(pixel);
};
//Add ignores
var ignoreArray = ["acid", "iron", "silver", "steel", "tungstensteel", "void", "liquid_void", "chute", "vute", "drute", "drolute", "volute", "alkahest", "acid_gas"];
for(l = 0; l < ignoreArray.length; l++) {
var name = ignoreArray[l];
var fairyName = `${ignoreArray[l]}_fairy`;
if(elementExists(name) && elementExists(fairyName)) {
var baseInfo = elements[name];
if(typeof(baseInfo.ignore) === "undefined") {
baseInfo.ignore = [];
} else if(typeof(baseInfo.ignore) === "string") {
baseInfo.ignore = [baseInfo.ignore];
};
baseInfo.ignore.push(fairyName);
};
};
//Update FAIRYKILL elements
var fairykillElements = ["iron", "silver", "steel", "tungstensteel"];
for(q = 0; q < fairykillElements.length; q++) {
var name = fairykillElements[q];
if(elementExists(name)) {
elements[name].behavior = behaviors.FAIRYKILL;
};
};
} else {
if(!enabledMods.includes(runAfterAutogenMod)) { enabledMods.splice(enabledMods.indexOf(modName),0,runAfterAutogenMod) };
if(!enabledMods.includes(explodeAtPlusMod)) { enabledMods.splice(enabledMods.indexOf(modName),0,explodeAtPlusMod) };
if(!enabledMods.includes(libraryMod)) { enabledMods.splice(enabledMods.indexOf(modName),0,libraryMod) };
if(!enabledMods.includes(feyAndMoreMod)) { enabledMods.splice(enabledMods.indexOf(modName),0,feyAndMoreMod) };
if(!enabledMods.includes(mobsMod)) { enabledMods.splice(enabledMods.indexOf(modName),0,mobsMod) };
alert(`The "${runAfterAutogenMod}", "${libraryMod}", "${explodeAtPlusMod}", "${feyAndMoreMod}", and "${mobsMod}" mods are required; any missing mods in this list have been automatically inserted (reload for this to take effect).`)
localStorage.setItem("enabledMods", JSON.stringify(enabledMods));
};