5148 lines
173 KiB
JavaScript
5148 lines
173 KiB
JavaScript
function whenAvailable(names, callback) {
|
|
var interval = 10; // ms
|
|
window.setTimeout(function() {
|
|
let bool = true;
|
|
for(let i = 0; i < names.length; i++)
|
|
{
|
|
if(!window[names[i]])
|
|
{
|
|
bool = false;
|
|
}
|
|
}
|
|
if (bool) {
|
|
callback();
|
|
} else {
|
|
whenAvailable(names, callback);
|
|
}
|
|
}, interval);
|
|
}
|
|
var modName = "mods/mobs.js";
|
|
var explodeAtPlusMod = "mods/explodeAtPlus.js";
|
|
var runAfterAutogenMod = "mods/runAfterAutogen2.js";
|
|
var libraryMod = "mods/code_library.js";
|
|
var mobsLoaded = false;
|
|
if(enabledMods.includes(runAfterAutogenMod) && enabledMods.includes(explodeAtPlusMod) && enabledMods.includes(libraryMod)) {
|
|
whenAvailable(["runAfterAutogen","explodeAtPlus"], function() {
|
|
//Prerequisite Functions and Variables
|
|
|
|
minimumCreeperTries = 3;
|
|
maximumCreeperTries = 3;
|
|
|
|
minimumZombieTries = 3;
|
|
maximumZombieTries = 3;
|
|
|
|
minimumSkeletonTries = 3;
|
|
maximumSkeletonTries = 3;
|
|
|
|
eLists.CREEPER = ["creeper", "angelic_creeper", "hell_creeper", "bombing_creeper", "baby_creeper"];
|
|
|
|
headBodyObject = {
|
|
"head": "body",
|
|
"creeper_head": "creeper_body",
|
|
"angelic_creeper_head": "angelic_creeper_body",
|
|
"hell_creeper_head": "hell_creeper_body",
|
|
"bombing_creeper_head": "bombing_creeper_body",
|
|
"zombie_head": "zombie_body",
|
|
"skeleton_head": "skeleton_body",
|
|
"nothing_there_phase_3_head": "nothing_there_phase_3_body",
|
|
};
|
|
|
|
var style = document.createElement('style'); //Initialize CSS for creeper spawning's status indicator
|
|
style.type = 'text/css';
|
|
style.id = 'creeperStatusStylesheet';
|
|
//initial style conditional branch
|
|
if(typeof(settings.creeperSpawning) === "undefined") { //undefined (falsy but it needs special handling)
|
|
style.innerHTML = '.creeperStatus { color: #E11; text-decoration: none; }';
|
|
} else {
|
|
if(!settings.creeperSpawning) { //falsy: red
|
|
style.innerHTML = '.creeperStatus { color: #E11; text-decoration: none; }';
|
|
} else if(settings.creeperSpawning) { //truthy: green
|
|
style.innerHTML = '.creeperStatus { color: #1E1; text-decoration: none; }';
|
|
};
|
|
};
|
|
document.getElementsByTagName('head')[0].appendChild(style);
|
|
|
|
function coordPyth(xA,yA,xB,yB) { //Distance function, used for explosion trigger
|
|
var a = Math.abs(xB - xA);
|
|
var b = Math.abs(yB - yA);
|
|
var c = Math.sqrt(a**2 + b**2);
|
|
return c;
|
|
};
|
|
|
|
function pythSpeed(number1,number2) {
|
|
return Math.sqrt(number1**2 + number2**2);
|
|
};
|
|
|
|
function rgbColorBound(number) { //RGB bounding function, used for safety checking color changes
|
|
return Math.min(255,Math.max(0,number));
|
|
};
|
|
|
|
function slBound(number) { //SL bounding function (not hue), same use as above
|
|
return Math.min(100,Math.max(0,number));
|
|
};
|
|
|
|
function angelicUpwardVelocity(pixel,x,y,radius,fire,smoke,power) { //Angelic Creeper's effect, "compatible" with velocity.js by including the modified version of its code in itself
|
|
var info = elements[pixel.element]
|
|
if(enabledMods.includes("mods/velocity.js")) {
|
|
//console.log("yeet");
|
|
// set the pixel.vx and pixel.vy depending on the angle and power
|
|
if (!elements[pixel.element].excludeRandom) {
|
|
//console.log("LOOKS LIKE IT'S YEETING TIME!");
|
|
var angle = Math.atan2(pixel.y-y,pixel.x-x);
|
|
//console.log(`angle calculated (${angle})`);
|
|
pixel.vx = Math.round((pixel.vx|0) + Math.cos(angle) * (radius * power/10));
|
|
//console.log(`vx calculated (${pixel.vx}) for pixel (${pixel.x},${pixel.y})`);
|
|
pixel.vy = 0 - Math.abs(Math.round((pixel.vy|0) + Math.sin(angle) * (radius * power/4)) + 4); //massively increased Y velocities even for objects below
|
|
//pixel.color = "rgb(255,0,0)";
|
|
//console.log(`vy calculated (${pixel.vy}) for pixel (${pixel.x},${pixel.y})`);
|
|
};
|
|
//console.log(`Velocities set`);
|
|
};
|
|
//console.log(`end`);
|
|
};
|
|
|
|
//afterFunction(pixel,x,y,radius,fire,smoke,power,damage);
|
|
function hellExplosionFire(pixel,x,y,radius,fire,smoke,power,damage) { //Angelic Creeper's effect, "compatible" with velocity.js by including the modified version of its code in itself
|
|
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]
|
|
if (info.burn) { //Light everything on fire
|
|
pixel.burning = true;
|
|
pixel.burnStart = pixelTicks;
|
|
pixel.temp += 10; //smoke prevention
|
|
} else if(Math.random() < 0.05) { //5%/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;
|
|
};
|
|
};
|
|
};
|
|
|
|
//explodeAtPlus moved to separate file
|
|
|
|
if(typeof(settings.creeperSpawning) === "undefined") { //Default creeper setting
|
|
setSetting("creeperSpawning",false);
|
|
};
|
|
|
|
function updateCreeperPreferences() { //Creeper setting handler
|
|
if(settings.creeperSpawning) { //If the setting is on
|
|
if(typeof(randomEvents.creeper) !== "function") { //add the event if it's missing
|
|
randomEvents.creeper = function() {
|
|
var amount = Math.floor((Math.random() * maximumCreeperTries)+minimumCreeperTries); //1-3
|
|
//In worldgen worlds, you can expect about half of this because about half of the world is pixels in it.
|
|
for(i = 0; i < amount; i++) { //dummy for to break
|
|
if(settings.creeperSpawning) { //setting validation
|
|
// random x between 1 and width-1
|
|
var x = Math.floor(Math.random()*(width-1))+1;
|
|
// random y between 1 and height
|
|
var y = Math.floor(Math.random()*height-1)+1;
|
|
if (isEmpty(x,y)) {
|
|
// random element from the list of spawnable creepers
|
|
var element = spawnCreepers[Math.floor(Math.random()*spawnCreepers.length)];
|
|
// if element is an array, choose a random element from the array
|
|
if (Array.isArray(element)) {
|
|
element = element[Math.floor(Math.random()*element.length)];
|
|
}
|
|
createPixel(element,x,y);
|
|
};
|
|
} else { //if false (this function is never supposed to fire with the setting false)
|
|
delete randomEvents.creeper; //self-disable
|
|
//substitute event
|
|
var event = randomEvents[Object.keys(randomEvents)[Math.floor(Math.random()*Object.keys(randomEvents).length)]];
|
|
event();
|
|
break;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
} else if(!settings.creeperSpawning) { //and if it's off
|
|
if(randomEvents.creeper) { delete randomEvents.creeper }; //delete it if it exists.
|
|
};
|
|
};
|
|
|
|
function toggleCreeperSpawning() { //Creeper toggle handler
|
|
if(settings.creeperSpawning != true) { //If it's false
|
|
setSetting("creeperSpawning",true); //make it true and update the status display CSS
|
|
updateCreeperPreferences(); //apply
|
|
document.getElementById("creeperStatusStylesheet").innerHTML = '.creeperStatus { color: #1E1; text-decoration: underline; }'; //Displayed info doen't update until it's pulled up again, so I'm using CSS to dynamically change the color of an element, like with find.js (RIP).
|
|
} else { //and the inverse if it's true
|
|
setSetting("creeperSpawning",false);
|
|
updateCreeperPreferences();
|
|
document.getElementById("creeperStatusStylesheet").innerHTML = '.creeperStatus { color: #E11; text-decoration: none; }';
|
|
};
|
|
};
|
|
|
|
//Functions used by Nothing There
|
|
|
|
function hasPixel(x,y,elementInput) {
|
|
if(isEmpty(x,y,true)) { //if empty, it can't have a pixel
|
|
return false;
|
|
} else {
|
|
if(elementInput.includes(",")) { //CSTA
|
|
elementInput = elementInput.split(",");
|
|
};
|
|
if(Array.isArray(elementInput)) { //if element list
|
|
return elementInput.includes(pixelMap[x][y].element);
|
|
} else { //if single element
|
|
return pixelMap[x][y].element === elementInput;
|
|
};
|
|
};
|
|
};
|
|
|
|
function breakPixel(pixel,changetemp=false,defaultBreakIntoDust=false) {
|
|
var info = elements[pixel.element];
|
|
if(typeof(info.breakInto) === "undefined") {
|
|
if(defaultBreakIntoDust) {
|
|
if(Math.random() < defaultBreakIntoDust) { changePixel(pixel,"dust",changetemp) };
|
|
};
|
|
return defaultBreakIntoDust;
|
|
};
|
|
var breakIntoElement = info.breakInto;
|
|
if(Array.isArray(breakIntoElement)) {
|
|
breakIntoElement = breakIntoElement[Math.floor(Math.random() * breakIntoElement.length)]
|
|
};
|
|
if(typeof(breakIntoElement) === "undefined" || breakIntoElement === null) {
|
|
deletePixel(pixel.x,pixel.y);
|
|
return true;
|
|
};
|
|
changePixel(pixel,breakIntoElement,changetemp)
|
|
return true;
|
|
};
|
|
|
|
defaultHardness = 0;
|
|
|
|
function arrowAltTb(pixel,breakChanceMultiplier,changetemp=false,defaultBreakIntoDust=false) {
|
|
var info = elements[pixel.element];
|
|
var hardness = defaultHardness;
|
|
if(typeof(info.hardness) === "number") {
|
|
hardness = info.hardness;
|
|
};
|
|
hardness = 1 - hardness; //invert hardness, so a hardness of 0 becomes a 100% chance and a hardness of 1 becomes a 0% chance
|
|
hardness *= breakChanceMultiplier;
|
|
if(Math.random() < hardness) {
|
|
return breakPixel(pixel,changetemp=false,defaultBreakIntoDust=false);
|
|
} else {
|
|
return false;
|
|
};
|
|
};
|
|
|
|
function nothingThereBulletMovement(pixel,x,y) {
|
|
if(!tryMove(pixel,x,y)) {
|
|
if(!isEmpty(x,y,true)) {
|
|
var thisDensity = elements[pixel.element].density;
|
|
var newPixel = pixelMap[x][y];
|
|
var newElement = newPixel.element;
|
|
var newInfo = elements[newElement];
|
|
var newHardness = 0;
|
|
if(nothingThereBulletExcludedElements.includes(newElement)) {
|
|
return false;
|
|
};
|
|
if(typeof(newInfo.hardness) === "number") {
|
|
newHardness = newInfo.hardness;
|
|
//it's inverted later
|
|
};
|
|
if(typeof(newInfo.state) === "undefined" && newElement !== pixel.element) { //Copy-paste of "break" code
|
|
if(Math.random() < ((1 - newHardness) ** 0.6)) {
|
|
swapPixels(pixel,newPixel);
|
|
//console.log(`nothingThereBulletMovement: Breaking pixel (${newPixel.element}, ${newPixel.x}, ${newPixel.y}))`)
|
|
breakPixel(newPixel,false,0.3);
|
|
return true;
|
|
} else {
|
|
deletePixel(pixel.x,pixel.y);
|
|
return false;
|
|
};
|
|
} else {
|
|
if(newElement == pixel.element) {
|
|
swapPixels(pixel,newPixel);
|
|
return true;
|
|
} else if(newInfo.state == "gas") {
|
|
swapPixels(pixel,newPixel);
|
|
return true;
|
|
} else if(newInfo.state == "liquid") {
|
|
var newDensity = 1000;
|
|
if(typeof(newInfo.density) === "number") {
|
|
newDensity = newInfo.density;
|
|
//console.log(`density ${newInfo.density} for ${newElement}`);
|
|
//} else {
|
|
//console.log(`undef density for ${newElement}, 1000 default`);
|
|
};
|
|
//console.log(`thisDensity: ${thisDensity}`);
|
|
//console.log(`newDensity: ${newDensity}`);
|
|
var chance = thisDensity/(thisDensity+newDensity);
|
|
//console.log(`${newElement}, ${chance}`)
|
|
if(Math.random() < chance) {
|
|
swapPixels(pixel,newPixel);
|
|
};
|
|
return true;
|
|
} else if(newInfo.state == "solid") {
|
|
if(Math.random() < ((1 - newHardness) ** 0.6)) {
|
|
swapPixels(pixel,newPixel);
|
|
//console.log(`nothingThereBulletMovement: Breaking pixel (${newPixel.element}, ${newPixel.x}, ${newPixel.y}))`)
|
|
breakPixel(newPixel,false,0.3);
|
|
return true;
|
|
} else {
|
|
deletePixel(pixel.x,pixel.y);
|
|
return false;
|
|
};
|
|
};
|
|
};
|
|
} else {
|
|
return false;
|
|
};
|
|
} else {
|
|
return true;
|
|
};
|
|
};
|
|
|
|
//End NT functions
|
|
|
|
nothingThereBulletExcludedElements = ["wall","nothing_there_phase_1","nothing_there_phase_2","nothing_there_phase_3_body","nothing_there_phase_3_head", "nothing_there_cleaver", "nothing_there_mace"];
|
|
|
|
enemyHumanoidArray = ["head","body"] //just in case
|
|
|
|
spawnCreepers = ["creeper","baby_creeper","angelic_creeper","bombing_creeper","hell_creeper"];
|
|
|
|
if(settings.creeperSpawning) { //creeper spawning option
|
|
randomEvents.creeper = function() {
|
|
var amount = Math.floor((Math.random() * maximumCreeperTries)+minimumCreeperTries); //1-3
|
|
for(i = 0; i < amount; i++) { //dummy for to break
|
|
if(settings.creeperSpawning) { //setting validation
|
|
// random x between 1 and width-1
|
|
var x = Math.floor(Math.random()*(width-1))+1;
|
|
// random y between 1 and height
|
|
var y = Math.floor(Math.random()*height-1)+1;
|
|
if (isEmpty(x,y)) {
|
|
// random element from the list of spawnable creepers
|
|
var element = spawnCreepers[Math.floor(Math.random()*spawnCreepers.length)];
|
|
// if element is an array, choose a random element from the array
|
|
if (Array.isArray(element)) {
|
|
element = element[Math.floor(Math.random()*element.length)];
|
|
}
|
|
createPixel(element,x,y);
|
|
};
|
|
} else { //if false (this function is never supposed to fire with the setting false)
|
|
delete randomEvents.creeper; //self-disable
|
|
//substitute event
|
|
var event = randomEvents[Object.keys(randomEvents)[Math.floor(Math.random()*Object.keys(randomEvents).length)]];
|
|
event();
|
|
break;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
standaloneSpawnCreeper = function(amount=1) {
|
|
/* The amount is the maximum amount of *attempts*. Often, less creepers will spawn due to things in the way.
|
|
In a generated world, which uses half of the space, you can expect about half of this number to spawn. */
|
|
for(i = 0; i < amount; i++) { //dummy for to break
|
|
// random x between 1 and width-1
|
|
var x = Math.floor(Math.random()*(width-1))+1;
|
|
// random y between 1 and height
|
|
var y = Math.floor(Math.random()*height-1)+1;
|
|
if (isEmpty(x,y)) {
|
|
// random element from the list of spawnable creepers
|
|
var element = spawnCreepers[Math.floor(Math.random()*spawnCreepers.length)];
|
|
// if element is an array, choose a random element from the array
|
|
if (Array.isArray(element)) {
|
|
element = element[Math.floor(Math.random()*element.length)];
|
|
}
|
|
createPixel(element,x,y);
|
|
};
|
|
};
|
|
};
|
|
|
|
//Prerequisite Functions and Variables
|
|
|
|
function headHasBody(pixel) {
|
|
var pX = pixel.x;
|
|
var pY = pixel.y;
|
|
//console.log("Checking head for body");
|
|
if(Object.keys(headBodyObject).includes(pixel.element)) {
|
|
//console.log("The head has a corresponding body element.");
|
|
if(typeof(pixelMap[pX][pY+1]) === "undefined") {
|
|
//console.log("The body's place is empty.");
|
|
return false;
|
|
} else {
|
|
var bodyPixel = pixelMap[pX][pY+1];
|
|
//console.log("The body's place is not empty.");
|
|
return (bodyPixel.element === headBodyObject[pixel.element]);
|
|
};
|
|
} else {
|
|
//console.log("The head does not have corresponding body element.");
|
|
return null;
|
|
};
|
|
};
|
|
|
|
function bodyHasHead(pixel) {
|
|
var pX = pixel.x;
|
|
var pY = pixel.y;
|
|
//console.log("Checking body for head");
|
|
if(Object.values(headBodyObject).includes(pixel.element)) {
|
|
//console.log("The body has a corresponding head element.");
|
|
if(typeof(pixelMap[pX][pY-1]) === "undefined") {
|
|
//console.log("The head's place is empty.");
|
|
return false;
|
|
} else {
|
|
var headPixel = pixelMap[pX][pY-1];
|
|
//console.log("The head's place is not empty.");
|
|
return (headPixel.element === getKeyByValue(headBodyObject,pixel.element));
|
|
};
|
|
} else {
|
|
//console.log("The body does not have corresponding head element.");
|
|
return null;
|
|
};
|
|
};
|
|
|
|
function zombifyHuman(pixel) {
|
|
var pX = pixel.x;
|
|
var pY = pixel.y;
|
|
if(!["head","body"].includes(pixel.element)) {
|
|
//console.log("Not part of a human");
|
|
return false;
|
|
} else {
|
|
if(pixel.element === "head") {
|
|
//console.log("Head");
|
|
if(headHasBody(pixel)) {
|
|
//console.log("There's also a body");
|
|
var body = pixelMap[pX][pY+1];
|
|
changePixel(body,"zombie_body",false);
|
|
//console.log("Body turned (head path)");
|
|
};
|
|
changePixel(pixel,"zombie_head");
|
|
//console.log("Head turned (head path)");
|
|
} else {
|
|
//console.log("Not head (i.e. body)");
|
|
if(bodyHasHead(pixel)) {
|
|
//console.log("There's also a head");
|
|
var head = pixelMap[pX][pY-1];
|
|
changePixel(head,"zombie_head",false);
|
|
//console.log("Head turned (body path)");
|
|
};
|
|
changePixel(pixel,"zombie_body");
|
|
//console.log("Body turned (body path)");
|
|
};
|
|
};
|
|
};
|
|
|
|
function dezombifyHuman(pixel) {
|
|
var pX = pixel.x;
|
|
var pY = pixel.y;
|
|
if(!["zombie_head","zombie_body"].includes(pixel.element)) {
|
|
return false;
|
|
} else {
|
|
if(pixel.element === "zombie_head") {
|
|
if(headHasBody(pixel)) {
|
|
var body = pixelMap[pX][pY+1];
|
|
changePixel(body,"body",false);
|
|
};
|
|
changePixel(pixel,"head");
|
|
} else {
|
|
if(bodyHasHead(pixel)) {
|
|
var head = pixelMap[pX][pY-1];
|
|
changePixel(head,"head",false);
|
|
};
|
|
changePixel(pixel,"body");
|
|
};
|
|
};
|
|
};
|
|
|
|
elements.spawner = {
|
|
color: "#1c3038",
|
|
breakInto: ["steel","steel","smoke",null,null,null,null,null],
|
|
properties: {
|
|
spawnCounter: 0,
|
|
spawnTime: 160,
|
|
spawn: null,
|
|
squadiusX: 4,
|
|
squadiusY: 4,
|
|
spawnTries: 4,
|
|
//spawnRangeMax: null, //Disabled by default for performance and because I can't think of how to make it count MPL elements towards the limit
|
|
},
|
|
tick: function(pixel) {
|
|
//validation
|
|
if(Array.isArray(pixel.spawn)) {
|
|
pixel.spawn = pixel.spawn.filter(function(e){ return elementExists(e) });
|
|
if(pixel.spawn.length == 0) {
|
|
pixel.spawn = null;
|
|
};
|
|
} else {
|
|
if(!elementExists(pixel.spawn)) {
|
|
pixel.spawn = null;
|
|
};
|
|
};
|
|
if(pixel.spawn === null) {
|
|
return false;
|
|
};
|
|
if(pixel.spawnCounter <= 0) {
|
|
//var pixelsOfSpawnElement = 0;
|
|
|
|
var newSpawn = pixel.spawn;
|
|
if(Array.isArray(newSpawn)) {
|
|
newSpawn = newSpawn[Math.floor(Math.random() * newSpawn.length)];
|
|
};
|
|
|
|
var xForMin = -1 * pixel.squadiusX;
|
|
var xForMax = pixel.squadiusX + 1;
|
|
var yForMin = -1 * pixel.squadiusY;
|
|
var yForMax = pixel.squadiusY + 1;
|
|
|
|
/*if(pixel.spawnRangeMax !== null) {
|
|
for(xOffset = xForMin; xOffset < xForMax; xOffset++) {
|
|
for(yOffset = yForMin; yOffset < yForMax; yOffset++) {
|
|
var newX = pixel.x + xOffset;
|
|
var newY = pixel.y + yOffset;
|
|
if(isEmpty(newX,newY,true) || outOfBounds(newX,newY)) {
|
|
continue;
|
|
};
|
|
newPixel = pixelMap[newX][newY];
|
|
if(newPixel.element == pixel.spawn || pixel.spawn.includes(newPixel.element)) {
|
|
pixelsOfSpawnElement++;
|
|
};
|
|
};
|
|
};
|
|
if(pixelsOfSpawnElement > pixel.spawnRangeMax) {
|
|
return false;
|
|
};
|
|
};*/
|
|
|
|
for(s = 0; s < pixel.spawnTries; s++) {
|
|
var randomX = pixel.x + randomIntegerBetweenTwoValues(0 - pixel.squadiusX, pixel.squadiusX);
|
|
var randomY = pixel.y + randomIntegerBetweenTwoValues(0 - pixel.squadiusY, pixel.squadiusY);
|
|
if(isEmpty(randomX,randomY,false)) {
|
|
createPixel(newSpawn,randomX,randomY);
|
|
};
|
|
};
|
|
pixel.spawnCounter = pixel.spawnTime;
|
|
} else {
|
|
pixel.spawnCounter--;
|
|
};
|
|
},
|
|
hardness: 0.7,
|
|
state: "solid",
|
|
}
|
|
|
|
if(enabledMods.includes("mods/fey_and_more.js")) {
|
|
elements.spawner.breakInto.push("magic");
|
|
};
|
|
|
|
elements.frozen_rotten_meat = {
|
|
color: ["#8FB588", "#8FA888"],
|
|
behavior: [
|
|
"XX|CR:plague,stench,stench%0.125 AND CH:meat>rotten_meat%1 AND CH:frozen_meat>frozen_rotten_meat%0.85|XX",
|
|
"SP%99 AND CH:meat>rotten_meat%1 AND CH:frozen_meat>frozen_rotten_meat%0.85|XX|SP%99 AND CH:meat>rotten_meat%1 AND CH:frozen_meat>frozen_rotten_meat%0.85",
|
|
"XX|M1 AND CH:meat>rotten_meat%1 AND CH:frozen_meat>frozen_rotten_meat%0.85|XX",
|
|
],
|
|
temp: -18,
|
|
tempHigh: 0,
|
|
stateHigh: "rotten_meat",
|
|
category:"food",
|
|
hidden:true,
|
|
state: "solid",
|
|
density: 1037.5,
|
|
};
|
|
|
|
elements.rotten_meat.tempLow = -18;
|
|
elements.rotten_meat.stateLow = "frozen_rotten_meat";
|
|
|
|
elements.zombie_blood = {
|
|
color: ["#d18228", "#9a9e2f"],
|
|
behavior: behaviors.LIQUID,
|
|
reactions: {
|
|
"vaccine": { "elem2":null, "chance": 0.01 },
|
|
"plague": { "elem2":null, "chance": 0.01 },
|
|
"virus": { "elem2":null, "chance": 0.01 },
|
|
"cancer": { "elem1":"cancer", "chance": 0.02 },
|
|
/*"rat": { "elem2":"infection", "chance":0.075 },
|
|
"flea": { "elem1":"infection", "chance":0.03 },
|
|
"dirt": { "elem1":null, "elem2":"mud" },
|
|
"sand": { "elem1":null, "elem2":"wet_sand" },
|
|
"mercury": { "elem1":"infection", "elem2":null, "chance":0.05 },
|
|
"carbon_dioxide": { "elem2":null, "chance":0.05 },
|
|
"alcohol": { "elem1":[null,"dna"], "chance":0.02 },*/
|
|
"oxygen": { "elem2":null, "chance":0.04 },
|
|
"blood": { "elem2":"zombie_blood", "chance":0.1 },
|
|
},
|
|
viscosity: 30,
|
|
tempHigh: 127.55,
|
|
stateHigh: ["steam","salt","oxygen","plague"],
|
|
tempLow: 0,
|
|
category:"liquids",
|
|
state: "liquid",
|
|
density: 1160,
|
|
stain: 0.06,
|
|
tick: function(pixel) {
|
|
if(Math.random() < 0.2) {
|
|
var pX = pixel.x;
|
|
var pY = pixel.y;
|
|
for(i = 0; i < adjacentCoords.length; i++) {
|
|
var coord = adjacentCoords[i];
|
|
var oX = coord[0];
|
|
var oY = coord[1];
|
|
var nX = pX+oX;
|
|
var nY = pY+oY;
|
|
if(isEmpty(nX,nY,true)) {
|
|
continue;
|
|
} else {
|
|
var newPixel = pixelMap[nX][nY];
|
|
var newElement = newPixel.element;
|
|
if(enemyHumanoidArray.includes(newElement)) {
|
|
if(Math.random() < 0.1) { zombifyHuman(newPixel) };
|
|
};
|
|
};
|
|
};
|
|
};
|
|
},
|
|
};
|
|
|
|
var style = document.createElement('style'); //Initialize CSS for zombie spawning's status indicator
|
|
style.type = 'text/css';
|
|
style.id = 'zombieStatusStylesheet';
|
|
//initial style conditional branch
|
|
if(typeof(settings.zombieSpawning) === "undefined") { //undefined (falsy but it needs special handling)
|
|
style.innerHTML = '.zombieStatus { color: #E11; text-decoration: none; }';
|
|
} else {
|
|
if(!settings.zombieSpawning) { //falsy: red
|
|
style.innerHTML = '.zombieStatus { color: #E11; text-decoration: none; }';
|
|
} else if(settings.zombieSpawning) { //truthy: green
|
|
style.innerHTML = '.zombieStatus { color: #1E1; text-decoration: none; }';
|
|
};
|
|
};
|
|
document.getElementsByTagName('head')[0].appendChild(style);
|
|
|
|
if(typeof(settings.zombieSpawning) === "undefined") { //Default zombie setting
|
|
setSetting("zombieSpawning",false);
|
|
};
|
|
|
|
function updateZombiePreferences() { //Zombie setting handler
|
|
if(settings.zombieSpawning) { //If the setting is on
|
|
if(typeof(randomEvents.zombie) !== "function") { //add the event if it's missing
|
|
randomEvents.zombie = function() {
|
|
var amount = Math.floor((Math.random() * maximumZombieTries)+minimumZombieTries); //1-3
|
|
//In worldgen worlds, you can expect about half of this because about half of the world is pixels in it.
|
|
for(i = 0; i < amount; i++) { //dummy for to break
|
|
if(settings.zombieSpawning) { //setting validation
|
|
// random x between 1 and width-1
|
|
var x = Math.floor(Math.random()*(width-1))+1;
|
|
// random y between 1 and height
|
|
var y = Math.floor(Math.random()*height-1)+1;
|
|
if (isEmpty(x,y)) {
|
|
// random element from the list of spawnable zombies
|
|
var element = spawnZombies[Math.floor(Math.random()*spawnZombies.length)];
|
|
// if element is an array, choose a random element from the array
|
|
if (Array.isArray(element)) {
|
|
element = element[Math.floor(Math.random()*element.length)];
|
|
}
|
|
createPixel(element,x,y);
|
|
};
|
|
} else { //if false (this function is never supposed to fire with the setting false)
|
|
delete randomEvents.zombie; //self-disable
|
|
//substitute event
|
|
var event = randomEvents[Object.keys(randomEvents)[Math.floor(Math.random()*Object.keys(randomEvents).length)]];
|
|
event();
|
|
break;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
} else if(!settings.zombieSpawning) { //and if it's off
|
|
if(randomEvents.zombie) { delete randomEvents.zombie }; //delete it if it exists.
|
|
};
|
|
};
|
|
|
|
function toggleZombieSpawning() { //Zombie toggle handler
|
|
if(settings.zombieSpawning != true) { //If it's false
|
|
setSetting("zombieSpawning",true); //make it true and update the status display CSS
|
|
updateZombiePreferences(); //apply
|
|
document.getElementById("zombieStatusStylesheet").innerHTML = '.zombieStatus { color: #1E1; text-decoration: underline; }'; //Displayed info doen't update until it's pulled up again, so I'm using CSS to dynamically change the color of an element, like with find.js (RIP).
|
|
} else { //and the inverse if it's true
|
|
setSetting("zombieSpawning",false);
|
|
updateZombiePreferences();
|
|
document.getElementById("zombieStatusStylesheet").innerHTML = '.zombieStatus { color: #E11; text-decoration: none; }';
|
|
};
|
|
};
|
|
|
|
spawnZombies = ["zombie","baby_zombie"];
|
|
|
|
if(settings.zombieSpawning) { //zombie spawning option
|
|
randomEvents.zombie = function() {
|
|
var amount = Math.floor((Math.random() * maximumZombieTries)+minimumZombieTries); //1-3
|
|
for(i = 0; i < amount; i++) { //dummy for to break
|
|
if(settings.zombieSpawning) { //setting validation
|
|
// random x between 1 and width-1
|
|
var x = Math.floor(Math.random()*(width-1))+1;
|
|
// random y between 1 and height
|
|
var y = Math.floor(Math.random()*height-1)+1;
|
|
if (isEmpty(x,y)) {
|
|
// random element from the list of spawnable zombies
|
|
var element = spawnZombies[Math.floor(Math.random()*spawnZombies.length)];
|
|
// if element is an array, choose a random element from the array
|
|
if (Array.isArray(element)) {
|
|
element = element[Math.floor(Math.random()*element.length)];
|
|
}
|
|
createPixel(element,x,y);
|
|
};
|
|
} else { //if false (this function is never supposed to fire with the setting false)
|
|
delete randomEvents.zombie; //self-disable
|
|
//substitute event
|
|
var event = randomEvents[Object.keys(randomEvents)[Math.floor(Math.random()*Object.keys(randomEvents).length)]];
|
|
event();
|
|
break;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
standaloneSpawnZombie = function(amount=1) {
|
|
/* The amount is the maximum amount of *attempts*. Often, less zombies will spawn due to things in the way.
|
|
In a generated world, which uses half of the space, you can expect about half of this number to spawn. */
|
|
for(i = 0; i < amount; i++) { //dummy for to break
|
|
// random x between 1 and width-1
|
|
var x = Math.floor(Math.random()*(width-1))+1;
|
|
// random y between 1 and height
|
|
var y = Math.floor(Math.random()*height-1)+1;
|
|
if (isEmpty(x,y)) {
|
|
// random element from the list of spawnable zombies
|
|
var element = spawnZombies[Math.floor(Math.random()*spawnZombies.length)];
|
|
// if element is an array, choose a random element from the array
|
|
if (Array.isArray(element)) {
|
|
element = element[Math.floor(Math.random()*element.length)];
|
|
}
|
|
createPixel(element,x,y);
|
|
};
|
|
};
|
|
};
|
|
|
|
var style = document.createElement('style'); //Initialize CSS for skeleton spawning's status indicator
|
|
style.type = 'text/css';
|
|
style.id = 'skeletonStatusStylesheet';
|
|
//initial style conditional branch
|
|
if(typeof(settings.skeletonSpawning) === "undefined") { //undefined (falsy but it needs special handling)
|
|
style.innerHTML = '.skeletonStatus { color: #E11; text-decoration: none; }';
|
|
} else {
|
|
if(!settings.skeletonSpawning) { //falsy: red
|
|
style.innerHTML = '.skeletonStatus { color: #E11; text-decoration: none; }';
|
|
} else if(settings.skeletonSpawning) { //truthy: green
|
|
style.innerHTML = '.skeletonStatus { color: #1E1; text-decoration: none; }';
|
|
};
|
|
};
|
|
document.getElementsByTagName('head')[0].appendChild(style);
|
|
|
|
if(typeof(settings.skeletonSpawning) === "undefined") { //Default skeleton setting
|
|
setSetting("skeletonSpawning",false);
|
|
};
|
|
|
|
function updateSkeletonPreferences() { //Skeleton setting handler
|
|
if(settings.skeletonSpawning) { //If the setting is on
|
|
if(typeof(randomEvents.skeleton) !== "function") { //add the event if it's missing
|
|
randomEvents.skeleton = function() {
|
|
var amount = Math.floor((Math.random() * maximumSkeletonTries)+minimumSkeletonTries); //1-3
|
|
//In worldgen worlds, you can expect about half of this because about half of the world is pixels in it.
|
|
for(i = 0; i < amount; i++) { //dummy for to break
|
|
if(settings.skeletonSpawning) { //setting validation
|
|
// random x between 1 and width-1
|
|
var x = Math.floor(Math.random()*(width-1))+1;
|
|
// random y between 1 and height
|
|
var y = Math.floor(Math.random()*height-1)+1;
|
|
if (isEmpty(x,y)) {
|
|
// random element from the list of spawnable skeletons
|
|
var element = spawnSkeletons[Math.floor(Math.random()*spawnSkeletons.length)];
|
|
// if element is an array, choose a random element from the array
|
|
if (Array.isArray(element)) {
|
|
element = element[Math.floor(Math.random()*element.length)];
|
|
}
|
|
createPixel(element,x,y);
|
|
};
|
|
} else { //if false (this function is never supposed to fire with the setting false)
|
|
delete randomEvents.skeleton; //self-disable
|
|
//substitute event
|
|
var event = randomEvents[Object.keys(randomEvents)[Math.floor(Math.random()*Object.keys(randomEvents).length)]];
|
|
event();
|
|
break;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
} else if(!settings.skeletonSpawning) { //and if it's off
|
|
if(randomEvents.skeleton) { delete randomEvents.skeleton }; //delete it if it exists.
|
|
};
|
|
};
|
|
|
|
function toggleSkeletonSpawning() { //Skeleton toggle handler
|
|
if(settings.skeletonSpawning != true) { //If it's false
|
|
setSetting("skeletonSpawning",true); //make it true and update the status display CSS
|
|
updateSkeletonPreferences(); //apply
|
|
document.getElementById("skeletonStatusStylesheet").innerHTML = '.skeletonStatus { color: #1E1; text-decoration: underline; }'; //Displayed info doen't update until it's pulled up again, so I'm using CSS to dynamically change the color of an element, like with find.js (RIP).
|
|
} else { //and the inverse if it's true
|
|
setSetting("skeletonSpawning",false);
|
|
updateSkeletonPreferences();
|
|
document.getElementById("skeletonStatusStylesheet").innerHTML = '.skeletonStatus { color: #E11; text-decoration: none; }';
|
|
};
|
|
};
|
|
|
|
spawnSkeletons = ["skeleton"];
|
|
|
|
if(settings.skeletonSpawning) { //skeleton spawning option
|
|
randomEvents.skeleton = function() {
|
|
var amount = Math.floor((Math.random() * maximumSkeletonTries)+minimumSkeletonTries); //1-3
|
|
for(i = 0; i < amount; i++) { //dummy for to break
|
|
if(settings.skeletonSpawning) { //setting validation
|
|
// random x between 1 and width-1
|
|
var x = Math.floor(Math.random()*(width-1))+1;
|
|
// random y between 1 and height
|
|
var y = Math.floor(Math.random()*height-1)+1;
|
|
if (isEmpty(x,y)) {
|
|
// random element from the list of spawnable skeletons
|
|
var element = spawnSkeletons[Math.floor(Math.random()*spawnSkeletons.length)];
|
|
// if element is an array, choose a random element from the array
|
|
if (Array.isArray(element)) {
|
|
element = element[Math.floor(Math.random()*element.length)];
|
|
}
|
|
createPixel(element,x,y);
|
|
};
|
|
} else { //if false (this function is never supposed to fire with the setting false)
|
|
delete randomEvents.skeleton; //self-disable
|
|
//substitute event
|
|
var event = randomEvents[Object.keys(randomEvents)[Math.floor(Math.random()*Object.keys(randomEvents).length)]];
|
|
event();
|
|
break;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
standaloneSpawnSkeleton = function(amount=1) {
|
|
/* The amount is the maximum amount of *attempts*. Often, less skeletons will spawn due to things in the way.
|
|
In a generated world, which uses half of the space, you can expect about half of this number to spawn. */
|
|
for(i = 0; i < amount; i++) { //dummy for to break
|
|
// random x between 1 and width-1
|
|
var x = Math.floor(Math.random()*(width-1))+1;
|
|
// random y between 1 and height
|
|
var y = Math.floor(Math.random()*height-1)+1;
|
|
if (isEmpty(x,y)) {
|
|
// random element from the list of spawnable skeletons
|
|
var element = spawnSkeletons[Math.floor(Math.random()*spawnSkeletons.length)];
|
|
// if element is an array, choose a random element from the array
|
|
if (Array.isArray(element)) {
|
|
element = element[Math.floor(Math.random()*element.length)];
|
|
}
|
|
createPixel(element,x,y);
|
|
};
|
|
};
|
|
};
|
|
|
|
/*Start Main Zombie
|
|
..................
|
|
.........###......
|
|
.......#######....
|
|
......#####OOOOO..
|
|
....######OOOOOOO.
|
|
...########OOOOO..
|
|
...###########....
|
|
...###########....
|
|
...###########....
|
|
....##########....
|
|
......########....
|
|
.......##...##....
|
|
.......##...##....
|
|
..................
|
|
*/
|
|
|
|
elements.zombie = {
|
|
color: ["#567C44","#199A9A","#41369B"],
|
|
category: "life",
|
|
properties: {
|
|
dead: false,
|
|
dir: 1,
|
|
panic: 0,
|
|
following: false,
|
|
},
|
|
movable: true,
|
|
tick: function(pixel) {
|
|
if (isEmpty(pixel.x, pixel.y+1)) {
|
|
createPixel("zombie_body", pixel.x, pixel.y+1);
|
|
pixel.element = "zombie_head";
|
|
pixel.color = pixelColorPick(pixel)
|
|
}
|
|
else if (isEmpty(pixel.x, pixel.y-1)) {
|
|
createPixel("zombie_head", pixel.x, pixel.y-1);
|
|
pixelMap[pixel.x][pixel.y-1].color = pixel.color;
|
|
pixel.element = "zombie_body";
|
|
pixel.color = pixelColorPick(pixel)
|
|
}
|
|
else {
|
|
deletePixel(pixel.x, pixel.y);
|
|
}
|
|
},
|
|
related: ["zombie_body","zombie_head"],
|
|
desc: "<em>I'd rather this be toggleable mid-game than require a reload.</em><br/><br/><span class=\"zombieStatus\">If this text is green or underlined, zombies (all types) can spawn.</span> <span onclick=toggleZombieSpawning() style=\"color: #ff00ff;\";>Click here</span> to toggle zombie spawning. If it's on, zombies can spawn through random events."
|
|
};
|
|
|
|
elements.zombie_body = {
|
|
color: "#27719D",
|
|
category: "life",
|
|
hidden: true,
|
|
density: 1500,
|
|
state: "solid",
|
|
conduct: 25,
|
|
tempHigh: 250,
|
|
stateHigh: "rotten_meat",
|
|
tempLow: -30,
|
|
stateLow: "frozen_rotten_meat",
|
|
burn: 10,
|
|
burnTime: 250,
|
|
burnInto: "rotten_meat",
|
|
breakInto: ["zombie_blood","rotten_meat"],
|
|
reactions: {
|
|
"cancer": { "elem1":"cancer", "chance":0.005 },
|
|
"radiation": { "elem1":["ash","rotten_meat","rotten_meat"], "chance":0.4 },
|
|
"plague": { "elem1":"plague", "chance":0.025 },
|
|
},
|
|
properties: {
|
|
dead: false,
|
|
dir: 1,
|
|
panic: 0,
|
|
},
|
|
movable: true,
|
|
tick: function(pixel) {
|
|
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 == "zombie_head") {
|
|
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 > 100
|
|
if (pixelTicks-pixel.dead > 100) {
|
|
changePixel(pixel,"rotten_meat");
|
|
};
|
|
return;
|
|
};
|
|
|
|
// Find the head
|
|
if (!isEmpty(pixel.x, pixel.y-1, true)) {
|
|
if(pixelMap[pixel.x][pixel.y-1].element == "head") {
|
|
changePixel(pixelMap[pixel.x][pixel.y-1],"zombie_head");
|
|
} else if(pixelMap[pixel.x][pixel.y-1].element == "zombie_head") {
|
|
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;
|
|
};
|
|
} else { var head = null };
|
|
|
|
if (isEmpty(pixel.x, pixel.y-1)) {
|
|
// create zombie blood if decapitated 10% chance
|
|
if (Math.random() < 0.1) {
|
|
createPixel("zombie_blood", pixel.x, pixel.y-1);
|
|
// set dead to true 10% chance
|
|
if (Math.random() < 0.10) {
|
|
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*");
|
|
};*/
|
|
};
|
|
},
|
|
};
|
|
|
|
elements.zombie_head = {
|
|
color: "#567C44",
|
|
category: "life",
|
|
hidden: true,
|
|
density: 1500,
|
|
state: "solid",
|
|
conduct: 25,
|
|
tempHigh: 250,
|
|
stateHigh: "rotten_meat",
|
|
tempLow: -30,
|
|
stateLow: "frozen_rotten_meat",
|
|
burn: 10,
|
|
burnTime: 250,
|
|
burnInto: "rotten_meat",
|
|
breakInto: ["zombie_blood","rotten_meat"],
|
|
reactions: {
|
|
"cancer": { "elem1":"cancer", "chance":0.005 },
|
|
"radiation": { "elem1":["ash","rotten_meat","rotten_meat"], "chance":0.4 },
|
|
"plague": { "elem1":"plague", "chance":0.025 },
|
|
},
|
|
properties: {
|
|
dead: false,
|
|
following: false,
|
|
dir: 1,
|
|
panic: 0,
|
|
},
|
|
movable: true,
|
|
tick: function(pixel) {
|
|
doHeat(pixel);
|
|
doBurning(pixel);
|
|
doElectricity(pixel);
|
|
if (pixel.dead) {
|
|
// Turn into rotten_meat if pixelTicks-dead > 100
|
|
if (pixelTicks-pixel.dead > 100) {
|
|
changePixel(pixel,"rotten_meat");
|
|
};
|
|
return;
|
|
};
|
|
|
|
// Find the body
|
|
if (!isEmpty(pixel.x, pixel.y+1, true)) {
|
|
if(pixelMap[pixel.x][pixel.y+1].element == "body") {
|
|
changePixel(pixelMap[pixel.x][pixel.y+1],"zombie_body");
|
|
} else if(pixelMap[pixel.x][pixel.y+1].element == "zombie_body") {
|
|
var body = pixelMap[pixel.x][pixel.y+1];
|
|
if (body.dead) { // If body is dead, kill body
|
|
pixel.dead = body.dead;
|
|
};
|
|
} else {
|
|
var body = null;
|
|
};
|
|
} 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 zombie blood if severed 10% chance
|
|
if (isEmpty(pixel.x, pixel.y+1) && !pixel.dead && Math.random() < 0.1) {
|
|
createPixel("zombie_blood", pixel.x, pixel.y+1);
|
|
// set dead to true 10% chance
|
|
if (Math.random() < 0.10) {
|
|
pixel.dead = pixelTicks;
|
|
}
|
|
}
|
|
}
|
|
|
|
//start of most new code
|
|
var pX = pixel.x;
|
|
var pY = pixel.y;
|
|
|
|
//Human detection loop (looks ahead according to direction and sets the "following" variable to true, telling the body to lock the direction)
|
|
if(pixelTicks % 2 == 0) { //reduce rate for performance
|
|
/*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 > (-35 - 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) { //If not dead
|
|
pixel.following = true;
|
|
//console.log(`Human detected at (${nX},${nY})`)
|
|
//Infect/kill if a human is close enough
|
|
if(coordPyth(pX,pY,nX,nY) <= 1.5) { //approx. sqrt(2)
|
|
if(Math.random() < 1/3) { //One-third chance to mutilate (changePixel)
|
|
if(Math.random() < 1/3) { //One-third chance to change to blood
|
|
changePixel(newPixel,"zombie_blood",false); //blood is turned in place
|
|
} else { //Remaining 2/3 chance to change to rotten flesh
|
|
changePixel(newPixel,"rotten_meat",false);
|
|
};
|
|
} else { //Remaining 2/3 chance to turn the human
|
|
zombifyHuman(newPixel);
|
|
};
|
|
};
|
|
} else { //Mutilate if dead
|
|
if(Math.random() < 1/3) { //One-third chance to change to blood
|
|
changePixel(newPixel,"zombie_blood",false); //blood is turned in place
|
|
} else { //Remaining 2/3 chance to change to rotten flesh
|
|
changePixel(newPixel,"rotten_meat",false);
|
|
};
|
|
};
|
|
} 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 < 35 + 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) { //If not dead
|
|
pixel.following = true;
|
|
//console.log(`Human detected at (${nX},${nY})`)
|
|
//Infect/kill if a human is close enough
|
|
if(coordPyth(pX,pY,nX,nY) <= 1.5) { //approx. sqrt(2)
|
|
if(Math.random() < 1/3) { //One-third chance to mutilate (changePixel)
|
|
if(Math.random() < 1/3) { //One-third chance to change to blood
|
|
changePixel(newPixel,"zombie_blood",false); //blood is turned in place
|
|
} else { //Remaining 2/3 chance to change to rotten flesh
|
|
changePixel(newPixel,"rotten_meat",false);
|
|
};
|
|
} else { //Remaining 2/3 chance to turn the human
|
|
zombifyHuman(newPixel);
|
|
};
|
|
};
|
|
} else { //Mutilate if dead
|
|
if(Math.random() < 1/3) { //One-third chance to change to blood
|
|
changePixel(newPixel,"zombie_blood",false); //blood is turned in place
|
|
} else { //Remaining 2/3 chance to change to rotten flesh
|
|
changePixel(newPixel,"rotten_meat",false);
|
|
};
|
|
};
|
|
} else {
|
|
//console.log(`Stopping row look at pixel (${nX},${nY}) due to non-human pixel in the way`)
|
|
break; //can't see through humans
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
if(Math.random() < 0.01) { //1% chance each tick to lose interest
|
|
pixel.following = false;
|
|
//console.log("Meh.");
|
|
};
|
|
},
|
|
};
|
|
|
|
//Baby Zombie
|
|
|
|
elements.baby_zombie = {
|
|
color: "#199A9A",
|
|
category: "life",
|
|
hidden: true,
|
|
density: 1500,
|
|
state: "solid",
|
|
conduct: 25,
|
|
tempHigh: 250,
|
|
stateHigh: "rotten_meat",
|
|
tempLow: -30,
|
|
stateLow: "frozen_rotten_meat",
|
|
burn: 10,
|
|
burnTime: 250,
|
|
burnInto: "rotten_meat",
|
|
breakInto: ["zombie_blood","rotten_meat"],
|
|
reactions: {
|
|
"cancer": { "elem1":"cancer", "chance":0.005 },
|
|
"radiation": { "elem1":["ash","rotten_meat","rotten_meat"], "chance":0.4 },
|
|
"plague": { "elem1":"plague", "chance":0.025 },
|
|
},
|
|
properties: {
|
|
dead: false,
|
|
dir: 1,
|
|
panic: 0,
|
|
},
|
|
movable: true,
|
|
tick: function(pixel) {
|
|
tryMove(pixel, pixel.x, pixel.y+1); // Fall
|
|
doHeat(pixel);
|
|
doBurning(pixel);
|
|
doElectricity(pixel);
|
|
if (pixel.dead) {
|
|
// Turn into rotten_meat if pixelTicks-dead > 100
|
|
if (pixelTicks-pixel.dead > 100) {
|
|
changePixel(pixel,"rotten_meat");
|
|
};
|
|
return;
|
|
};
|
|
|
|
if (Math.random() < 0.15) { // Move 10% chance
|
|
var movesToTry = [
|
|
[1*pixel.dir,0], //dash move
|
|
[1*pixel.dir,-1], //slash move
|
|
];
|
|
// 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])) {
|
|
break;
|
|
};
|
|
};
|
|
};
|
|
// 15% chance to change direction while not chasing a human
|
|
if(!pixel.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*");
|
|
};*/
|
|
};
|
|
|
|
var pX = pixel.x;
|
|
var pY = pixel.y;
|
|
|
|
//Human detection loop (looks ahead according to direction and sets the "following" variable to true, telling the body to lock the direction)
|
|
if(pixelTicks % 2 == 0) { //reduce rate for performance
|
|
/*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 > (-35 - 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) { //If not dead
|
|
pixel.following = true;
|
|
//console.log(`Human detected at (${nX},${nY})`)
|
|
//Infect/kill if a human is close enough
|
|
if(coordPyth(pX,pY,nX,nY) <= 1.5) { //approx. sqrt(2)
|
|
if(Math.random() < 1/3) { //One-third chance to mutilate (changePixel)
|
|
if(Math.random() < 1/4) { //One-fourth chance to change to blood
|
|
changePixel(newPixel,"zombie_blood",false); //blood is turned in place
|
|
} else { //Remaining 3/4 chance to change to rotten flesh
|
|
changePixel(newPixel,"rotten_meat",false);
|
|
};
|
|
} else { //Remaining 2/3 chance to turn the human
|
|
zombifyHuman(newPixel);
|
|
};
|
|
};
|
|
} else { //Mutilate if dead
|
|
if(Math.random() < 1/4) { //One-fourth chance to change to blood
|
|
changePixel(newPixel,"zombie_blood",false); //blood is turned in place
|
|
} else { //Remaining 3/4 chance to change to rotten flesh
|
|
changePixel(newPixel,"rotten_meat",false);
|
|
};
|
|
};
|
|
} 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 < 35 + 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) { //If not dead
|
|
pixel.following = true;
|
|
//console.log(`Human detected at (${nX},${nY})`)
|
|
//Infect/kill if a human is close enough
|
|
if(coordPyth(pX,pY,nX,nY) <= 1.5) { //approx. sqrt(2)
|
|
if(Math.random() < 1/3) { //One-third chance to mutilate (changePixel)
|
|
if(Math.random() < 1/4) { //One-fourth chance to change to blood
|
|
changePixel(newPixel,"zombie_blood",false); //blood is turned in place
|
|
} else { //Remaining 3/4 chance to change to rotten flesh
|
|
changePixel(newPixel,"rotten_meat",false);
|
|
};
|
|
} else { //Remaining 2/3 chance to turn the human
|
|
zombifyHuman(newPixel);
|
|
};
|
|
};
|
|
} else { //Mutilate if dead
|
|
if(Math.random() < 1/4) { //One-fourth chance to change to blood
|
|
changePixel(newPixel,"zombie_blood",false); //blood is turned in place
|
|
} else { //Remaining 3/4 chance to change to rotten flesh
|
|
changePixel(newPixel,"rotten_meat",false);
|
|
};
|
|
};
|
|
} else {
|
|
//console.log(`Stopping row look at pixel (${nX},${nY}) due to non-human pixel in the way`)
|
|
break; //can't see through humans
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
if(Math.random() < 0.01) { //1% chance each tick to lose interest
|
|
pixel.following = false;
|
|
//console.log("Meh.");
|
|
};
|
|
},
|
|
related: ["zombie"],
|
|
desc: "Baby zombies: smaller, faster, and more annoying.",
|
|
};
|
|
|
|
/*Start Main Creeper
|
|
##################
|
|
######### ######
|
|
####### ####
|
|
###### OOOOO##
|
|
#### OOOOOOO#
|
|
### OOOOO##
|
|
### ####
|
|
### ####
|
|
### ####
|
|
#### ####
|
|
###### ####
|
|
####### ### ####
|
|
####### ### ####
|
|
##################
|
|
*/
|
|
|
|
elements.creeper = {
|
|
color: ["#D2D2D2", "#BFDFB9", "#94CE89", "#78D965", "#5ED54C", "#58C546", "#50B143", "#479143", "#559552", "#3F8738", "#5B8B59"],
|
|
category: "life",
|
|
properties: {
|
|
dead: false,
|
|
dir: 1,
|
|
panic: 0,
|
|
following: false,
|
|
},
|
|
movable: true,
|
|
tick: function(pixel) {
|
|
if (isEmpty(pixel.x, pixel.y+1)) {
|
|
createPixel("creeper_body", pixel.x, pixel.y+1);
|
|
pixel.element = "creeper_head";
|
|
pixel.color = pixelColorPick(pixel)
|
|
}
|
|
else if (isEmpty(pixel.x, pixel.y-1)) {
|
|
createPixel("creeper_head", pixel.x, pixel.y-1);
|
|
pixelMap[pixel.x][pixel.y-1].color = pixel.color;
|
|
pixel.element = "creeper_body";
|
|
pixel.color = pixelColorPick(pixel)
|
|
}
|
|
else {
|
|
deletePixel(pixel.x, pixel.y);
|
|
}
|
|
},
|
|
related: ["creeper_body","creeper_head"],
|
|
desc: "<em>I'd rather this be toggleable mid-game than require a reload.</em><br/><br/><span class=\"creeperStatus\">If this text is green or underlined, creepers can spawn.</span> <span onclick=toggleCreeperSpawning() style=\"color: #ff00ff;\";>Click here</span> to toggle creeper spawning. If it's on, creepers (all types) can spawn through random events.<br/>To enable automatic creeper generation, set the generateCreepers query parameter."
|
|
};
|
|
|
|
elements.creeper_body = {
|
|
color: ["#D2D2D2", "#BFDFB9", "#94CE89", "#78D965", "#5ED54C", "#58C546", "#50B143", "#479143", "#559552", "#3F8738", "#5B8B59"],
|
|
category: "life",
|
|
hidden: 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,
|
|
},
|
|
movable: true,
|
|
tick: function(pixel) {
|
|
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 == "creeper_head") {
|
|
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 == "creeper_head") {
|
|
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);
|
|
//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");
|
|
};
|
|
//}
|
|
};
|
|
};
|
|
},
|
|
};
|
|
|
|
elements.creeper_head = {
|
|
color: ["#5B8B59", "#3F8738", "#559552", "#479143", "#50B143", "#58C546"],
|
|
category: "life",
|
|
hidden: 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,
|
|
},
|
|
movable: true,
|
|
tick: function(pixel) {
|
|
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 == "creeper_body") {
|
|
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 radius ${explosionRadius} (charged: ${pixel.charged})`);
|
|
explodeAt(body.x,body.y,explosionRadius);
|
|
//console.log("Yes, Rico, kaboom.");
|
|
};
|
|
};
|
|
};
|
|
|
|
if(Math.random() < 0.01) { //1% chance each tick to lose interest
|
|
pixel.following = false;
|
|
//console.log("Meh.");
|
|
};
|
|
},
|
|
};
|
|
|
|
/*End Main Creeper
|
|
##################
|
|
##################
|
|
##################
|
|
#########X#X######
|
|
#########XXX######
|
|
##########X#######
|
|
##########X#######
|
|
### # # # X # ####
|
|
### ####
|
|
#### ####
|
|
###### ####
|
|
####### ### ####
|
|
####### ### ####
|
|
##################
|
|
*/
|
|
|
|
//Baby Creeper
|
|
|
|
elements.baby_creeper = {
|
|
color: ["#D2D2D2", "#BFDFB9", "#94CE89", "#78D965", "#5ED54C", "#58C546", "#50B143", "#479143", "#559552", "#3F8738", "#5B8B59"],
|
|
category: "life",
|
|
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,
|
|
},
|
|
movable: true,
|
|
tick: function(pixel) {
|
|
tryMove(pixel, pixel.x, pixel.y+1); // Fall
|
|
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
|
|
}
|
|
|
|
if (Math.random() < 0.15) { // Move 15% chance (should be 12.5 but 15 looks better)
|
|
var movesToTry = [
|
|
[1*pixel.dir,0], //dash move
|
|
[1*pixel.dir,-1], //slash move
|
|
];
|
|
// 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])) {
|
|
break;
|
|
};
|
|
};
|
|
};
|
|
// 15% chance to change direction while not chasing a human
|
|
if(!pixel.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(typeof(pixel.charged) === "undefined") {
|
|
pixel.charged = false;
|
|
};
|
|
|
|
if(pixel.charge) {
|
|
pixel.charged = true;
|
|
};
|
|
|
|
var pX = pixel.x;
|
|
var pY = pixel.y;
|
|
|
|
if(pixel.charged) {
|
|
var explosionRadius = 6;
|
|
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 = 4; //should be half of the original creeper's radius
|
|
};
|
|
|
|
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 > 15) {
|
|
//console.log("Kaboom?");
|
|
explodeAt(pixel.x,pixel.y,explosionRadius);
|
|
//console.log("Yes, Rico, kaboom.");
|
|
};
|
|
};
|
|
|
|
//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) {
|
|
//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 + (2 * ticksHissing));
|
|
green = rgbColorBound(green + (2 * ticksHissing));
|
|
blue = rgbColorBound(blue + (2 * 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 + (2 * 1.176));
|
|
color = `hsl(${hue},${saturation}%,${luminance}%)`;
|
|
pixel.color = color;
|
|
//console.log("color set");
|
|
};
|
|
//}
|
|
|
|
if(pixelTicks - pixel.hissStart > 15) {
|
|
//console.log("Kaboom?");
|
|
//console.log(`Exploding with radius ${explosionRadius} (charged: ${pixel.charged})`);
|
|
explodeAt(pixel.x,pixel.y,explosionRadius);
|
|
//console.log("Yes, Rico, kaboom.");
|
|
};
|
|
};
|
|
};
|
|
|
|
if(Math.random() < 0.01) { //1% chance each tick to lose interest
|
|
pixel.following = false;
|
|
//console.log("Meh.");
|
|
};
|
|
|
|
//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;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
},
|
|
related: ["creeper"],
|
|
};
|
|
|
|
//Angelic Creeper
|
|
|
|
elements.angelic_creeper = { //let's get this one out of the way first
|
|
color: ["#f5ef56", "#fcbddf", "#de8aa8", "#e35d95", "#eb4974", "#ed3ea7", "#d645a3", "#a84556", "#9e4f6c", "#91315b", "#8c4963"],
|
|
category: "life",
|
|
properties: {
|
|
dead: false,
|
|
dir: 1,
|
|
panic: 0,
|
|
following: false,
|
|
},
|
|
movable: true,
|
|
tick: function(pixel) {
|
|
if (isEmpty(pixel.x, pixel.y+1)) {
|
|
createPixel("angelic_creeper_body", pixel.x, pixel.y+1);
|
|
pixel.element = "angelic_creeper_head";
|
|
pixel.color = pixelColorPick(pixel)
|
|
}
|
|
else if (isEmpty(pixel.x, pixel.y-1)) {
|
|
createPixel("angelic_creeper_head", pixel.x, pixel.y-1);
|
|
pixelMap[pixel.x][pixel.y-1].color = pixel.color;
|
|
pixel.element = "angelic_creeper_body";
|
|
pixel.color = pixelColorPick(pixel)
|
|
}
|
|
else {
|
|
deletePixel(pixel.x, pixel.y);
|
|
}
|
|
},
|
|
related: ["angelic_creeper_body","angelic_creeper_head"],
|
|
desc: 'A creeper type from <em>Extra Creeper Types</em> <a href="https://www.curseforge.com/minecraft/mc-mods/extra-creeper-types">(CF)</a>. It sends things upward.'
|
|
};
|
|
|
|
elements.angelic_creeper_body = {
|
|
color: ["#d2d2d2", "#fcbddf", "#de8aa8", "#e35d95", "#eb4974", "#ed3ea7", "#d645a3", "#a84556", "#9e4f6c", "#91315b", "#8c4963"],
|
|
category: "life",
|
|
hidden: 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","blood","gunpowder","gunpowder","feather"],
|
|
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,
|
|
},
|
|
movable: true,
|
|
tick: function(pixel) {
|
|
if(!pixel.hissing) { //If not hissing (it floats when hissing)
|
|
if(Math.random() < 0.2) { //20% chance to fall
|
|
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 == "angelic_creeper_head") {
|
|
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]);
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
} else {
|
|
if((pixelTicks - pixel.start) % 3 == 0) {
|
|
if (!isEmpty(pixel.x, pixel.y-1, true)) { // Find head
|
|
var headPixel = pixelMap[pixel.x][pixel.y-1];
|
|
if (headPixel.element == "angelic_creeper_head") { //Validate head
|
|
if (tryMove(headPixel, pixel.x, pixel.y-2)) { // Float
|
|
if (isEmpty(pixel.x, pixel.y-1)) { //If the head didn't swap with something
|
|
movePixel(pixel, pixel.x, pixel.y-1); //Pull body up
|
|
} else { //If it did swap
|
|
swapPixels(pixel, pixelMap[pixel.x][pixel.y-1]); //Pull body up through other pixel
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
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 == "angelic_creeper_head") {
|
|
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 = 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;
|
|
};
|
|
|
|
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("GOTTA YEET YEET YEET!");
|
|
explodeAtPlus(pixel.x,pixel.y,explosionRadius,"fire","smoke",null,angelicUpwardVelocity); //Special effect: Flings you upwards (extended to all movable tiles because it's easier).
|
|
//It also floats when hissing, but that will come soon.
|
|
//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");
|
|
};
|
|
//}
|
|
};
|
|
};
|
|
},
|
|
},
|
|
|
|
elements.angelic_creeper_head = {
|
|
color: ["#f5ef56", "#f0ea4f", "#f0ea60"],
|
|
category: "life",
|
|
hidden: 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","blood","blood","blood","feather"],
|
|
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,
|
|
},
|
|
movable: true,
|
|
tick: function(pixel) {
|
|
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 == "angelic_creeper_body") {
|
|
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("GOTTA YEET YEET YEET!");
|
|
//console.log(`Exploding with radius ${explosionRadius} (charged: ${pixel.charged})`);
|
|
explodeAtPlus(body.x,body.y,explosionRadius,"fire","smoke",null,angelicUpwardVelocity);
|
|
//console.log("Yes, Rico, kaboom.");
|
|
};
|
|
};
|
|
};
|
|
|
|
if(Math.random() < 0.01) { //1% chance each tick to lose interest
|
|
pixel.following = false;
|
|
//console.log("Meh.");
|
|
};
|
|
},
|
|
};
|
|
|
|
//Bombing Creeper
|
|
|
|
elements.bombing_creeper = {
|
|
color: ["#5b8b59", "#3f8738", "#559552", "#479143", "#50b143", "#58c546", "#e83c3c", "#c92a2a", "#f53d3d", "#ad3131"],
|
|
category: "life",
|
|
properties: {
|
|
dead: false,
|
|
dir: 1,
|
|
panic: 0,
|
|
following: false,
|
|
},
|
|
movable: true,
|
|
tick: function(pixel) {
|
|
if (isEmpty(pixel.x, pixel.y+1)) {
|
|
createPixel("bombing_creeper_body", pixel.x, pixel.y+1);
|
|
pixel.element = "bombing_creeper_head";
|
|
pixel.color = pixelColorPick(pixel)
|
|
}
|
|
else if (isEmpty(pixel.x, pixel.y-1)) {
|
|
createPixel("bombing_creeper_head", pixel.x, pixel.y-1);
|
|
pixelMap[pixel.x][pixel.y-1].color = pixel.color;
|
|
pixel.element = "bombing_creeper_body";
|
|
pixel.color = pixelColorPick(pixel)
|
|
}
|
|
else {
|
|
deletePixel(pixel.x, pixel.y);
|
|
}
|
|
},
|
|
related: ["bombing_creeper_body","bombing_creeper_head"],
|
|
desc: 'A creeper type from <em>Extra Creeper Types</em> <a href="https://www.curseforge.com/minecraft/mc-mods/extra-creeper-types">(CF)</a>. It spawns more explosives when it explodes.'
|
|
};
|
|
|
|
elements.bombing_creeper_body = {
|
|
color: ["#e83c3c", "#c92a2a", "#f53d3d", "#ad3131"],
|
|
category: "life",
|
|
hidden: 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","dynamite","gunpowder"],
|
|
breakInto: ["blood","dynamite","dynamite"],
|
|
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,
|
|
},
|
|
movable: true,
|
|
tick: function(pixel) {
|
|
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 == "bombing_creeper_head") {
|
|
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 == "bombing_creeper_head") {
|
|
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 = 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;
|
|
};
|
|
|
|
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,"fire,dynamite"); //Effect: Places (originally 5) primed TNT when it explodes (i.e. cluster bomb creeper)
|
|
//Dynamite is the closest thing we have to powder TNT (i.e. good enough)
|
|
//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");
|
|
};
|
|
//}
|
|
};
|
|
};
|
|
},
|
|
};
|
|
|
|
elements.bombing_creeper_head = {
|
|
color: ["#5B8B59", "#3F8738", "#559552", "#479143", "#50B143", "#58C546"],
|
|
category: "life",
|
|
hidden: 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","dynamite","gunpowder"],
|
|
breakInto: ["blood","dynamite"],
|
|
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,
|
|
},
|
|
movable: true,
|
|
tick: function(pixel) {
|
|
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 == "bombing_creeper_body") {
|
|
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 radius ${explosionRadius} (charged: ${pixel.charged})`);
|
|
explodeAt(body.x,body.y,explosionRadius,"fire,dynamite");
|
|
//console.log("Yes, Rico, kaboom.");
|
|
};
|
|
};
|
|
};
|
|
|
|
if(Math.random() < 0.01) { //1% chance each tick to lose interest
|
|
pixel.following = false;
|
|
//console.log("Meh.");
|
|
};
|
|
},
|
|
};
|
|
|
|
//Hell Creeper
|
|
|
|
elements.hell_creeper = {
|
|
color: ["#D2D2D2", "#ff141e", "#fc3232", "#DFAFAF", "#e84a4a", "#ce7979", "#d95555", "#d53c3c", "#c53636", "#b13333", "#913535", "#954242", "#872828", "#8b4949", "#2b0304"],
|
|
category: "life",
|
|
properties: {
|
|
dead: false,
|
|
dir: 1,
|
|
panic: 0,
|
|
following: false,
|
|
},
|
|
movable: true,
|
|
tick: function(pixel) {
|
|
if (isEmpty(pixel.x, pixel.y+1)) {
|
|
createPixel("hell_creeper_body", pixel.x, pixel.y+1);
|
|
pixel.element = "hell_creeper_head";
|
|
pixel.color = pixelColorPick(pixel)
|
|
}
|
|
else if (isEmpty(pixel.x, pixel.y-1)) {
|
|
createPixel("hell_creeper_head", pixel.x, pixel.y-1);
|
|
pixelMap[pixel.x][pixel.y-1].color = pixel.color;
|
|
pixel.element = "hell_creeper_body";
|
|
pixel.color = pixelColorPick(pixel)
|
|
}
|
|
else {
|
|
deletePixel(pixel.x, pixel.y);
|
|
}
|
|
},
|
|
related: ["hell_creeper_body","hell_creeper_head"],
|
|
desc: 'A creeper type from <em>Extra Creeper Types</em> <a href="https://www.curseforge.com/minecraft/mc-mods/extra-creeper-types">(CF)</a>. It has a small explosion radius, but spawns a lot of fire around its explosion.'
|
|
};
|
|
|
|
elements.hell_creeper_body = {
|
|
color: ["#D2D2D2", "#ff141e", "#fc3232", "#DFAFAF", "#e84a4a", "#ce7979", "#d95555", "#d53c3c", "#c53636", "#b13333", "#913535", "#954242", "#872828", "#8b4949", "#2b0304"],
|
|
category: "life",
|
|
hidden: true,
|
|
density: 1500,
|
|
state: "solid",
|
|
conduct: 25,
|
|
tempHigh: 2000, //they are immune to lava, and minecraft's lava is presumably mafic, so at least 1200*C
|
|
stateHigh: "ash",
|
|
breakInto: ["blood","gunpowder","fire"],
|
|
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,
|
|
},
|
|
movable: true,
|
|
tick: function(pixel) {
|
|
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 == "hell_creeper_head") {
|
|
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 == "hell_creeper_head") {
|
|
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 = 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;
|
|
};
|
|
|
|
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?");
|
|
explodeAtPlus(pixel.x,pixel.y,explosionRadius,"fire","fire",null,hellExplosionFire);
|
|
//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");
|
|
};
|
|
//}
|
|
};
|
|
};
|
|
},
|
|
};
|
|
|
|
elements.hell_creeper_head = {
|
|
color: ["#D2D2D2", "#ff141e", "#fc3232", "#e84a4a", "#b13333", "#913535", "#954242", "#872828", "#8b4949", "#2b0304", "#111111", "#faae3c", "#f5e131"],
|
|
category: "life",
|
|
hidden: true,
|
|
density: 1080,
|
|
state: "solid",
|
|
conduct: 25,
|
|
tempHigh: 2000,
|
|
stateHigh: "ash",
|
|
tempLow: -30,
|
|
stateLow: "frozen_meat",
|
|
breakInto: ["blood","fire"],
|
|
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,
|
|
},
|
|
movable: true,
|
|
tick: function(pixel) {
|
|
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 == "hell_creeper_body") {
|
|
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 radius ${explosionRadius} (charged: ${pixel.charged})`);
|
|
explodeAtPlus(pixel.x,pixel.y,explosionRadius,"fire","fire",null,hellExplosionFire);
|
|
//console.log("Yes, Rico, kaboom.");
|
|
};
|
|
};
|
|
};
|
|
|
|
if(Math.random() < 0.01) { //1% chance each tick to lose interest
|
|
pixel.following = false;
|
|
//console.log("Meh.");
|
|
};
|
|
},
|
|
};
|
|
|
|
/* +-----------------------------------+
|
|
| Nothing There |
|
|
| |
|
|
| amogus |
|
|
| |
|
|
| red imposter |
|
|
| |
|
|
| |
|
|
| |
|
|
| |
|
|
| |
|
|
| |
|
|
| |
|
|
+-----------------------------------+ */
|
|
|
|
elements.nothing_there_bullet = {
|
|
flippableX: true,
|
|
movable: true,
|
|
density: 10000,
|
|
desc: "A hypersonic bullet made of Nothing There's flesh. I don't remember if it can turn humans into red clouds.",
|
|
color: "#a3281a",
|
|
related: ["nothing_there_phase_3_body","nothing_there_phase_3_head"],
|
|
movable: true,
|
|
tick: function(pixel) {
|
|
if(typeof(pixel.flipX) == undefined) {
|
|
pixel.flipX = !!Math.floor(Math.random() * 2);
|
|
};
|
|
var dir = pixel.flipX ? -1 : 1;
|
|
for(i = 0; i < 6; i++) {
|
|
if(outOfBounds(pixel.x+dir,pixel.y)) {
|
|
deletePixel(pixel.x,pixel.y);
|
|
break;
|
|
};
|
|
if(!nothingThereBulletMovement(pixel,pixel.x+dir,pixel.y)) {
|
|
return true;
|
|
};
|
|
};
|
|
},
|
|
};
|
|
|
|
elements.nothing_there_mace = {
|
|
movable: true,
|
|
density: 10000,
|
|
desc: "A spiky mace attached to Nothing There, which can turn humans into red clouds.",
|
|
color: "#fa4632",
|
|
properties: {
|
|
counter: 2,
|
|
},
|
|
related: ["nothing_there_phase_3_body","nothing_there_phase_3_head"],
|
|
movable: true,
|
|
tick: function(pixel) {
|
|
if(outOfBounds(pixel.x,pixel.y + 1)) {
|
|
deletePixel(pixel.x,pixel.y);
|
|
return false;
|
|
};
|
|
if(!tryMove(pixel,pixel.x,pixel.y + 1)) {
|
|
var newPixel = pixelMap[pixel.x][pixel.y + 1];
|
|
var newElement = newPixel.element;
|
|
var newInfo = elements[newElement];
|
|
if(newElement !== pixel.element) {
|
|
if(newInfo.state === "gas") {
|
|
swapPixels(pixel,newPixel);
|
|
} else {
|
|
if(pixel.counter > 0) {
|
|
explodeAtPlus(pixel.x,pixel.y + 1,5,null,null);
|
|
pixel.counter--;
|
|
} else {
|
|
deletePixel(pixel.x,pixel.y);
|
|
return true;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
},
|
|
};
|
|
|
|
elements.nothing_there_cleaver = {
|
|
movable: true,
|
|
density: 10000,
|
|
desc: "A very sharp blade attached to Nothing There, which can turn humans into red clouds.",
|
|
color: "#a33c3c",
|
|
properties: {
|
|
counter: 4,
|
|
},
|
|
related: ["nothing_there_phase_3_body","nothing_there_phase_3_head"],
|
|
movable: true,
|
|
tick: function(pixel) {
|
|
if(outOfBounds(pixel.x,pixel.y + 1)) {
|
|
deletePixel(pixel.x,pixel.y);
|
|
return false;
|
|
};
|
|
if(!tryMove(pixel,pixel.x,pixel.y + 1)) {
|
|
var newPixel = pixelMap[pixel.x][pixel.y + 1];
|
|
var newElement = newPixel.element;
|
|
var newInfo = elements[newElement];
|
|
if(!nothingThereBulletExcludedElements.includes(newElement)) {
|
|
if(pixel.counter > 0) {
|
|
swapPixels(pixel,newPixel);
|
|
breakPixel(newPixel,false,false);
|
|
pixel.counter--;
|
|
} else {
|
|
deletePixel(pixel.x,pixel.y);
|
|
return true;
|
|
};
|
|
} else {
|
|
deletePixel(pixel.x,pixel.y);
|
|
return false;
|
|
};
|
|
};
|
|
},
|
|
};
|
|
|
|
testSwapArray = ["meat","cooked_meat","rotten_meat","blood","infection","antibody","plague","zombie_blood","frozen_meat","frozen_rotten_meat"];
|
|
|
|
elements.nothing_there_phase_1 = {
|
|
color: "#faacac",
|
|
category: "life",
|
|
density: 2000,
|
|
desc: "O-06-20 <span style='color: red;'>(ALEPH)</span><br/>In this phase, it looks like a dog made of misshapen human parts. It can easily turn humans into unrecognizable messes.",
|
|
state: "solid",
|
|
tempHigh: 3000,
|
|
hardness: 0.995,
|
|
stateHigh: "cooked_meat",
|
|
burn: 1,
|
|
burnTime: 250000,
|
|
burnInto: "cooked_meat",
|
|
breakInto: (enabledMods.includes("mods/fey_and_more.js") ? ["blood","meat","magic"] : ["blood","meat"]),
|
|
reactions: {
|
|
"cancer": { "elem1":"cancer", "chance":0.00002 },
|
|
"radiation": { "elem1":["ash","meat","rotten_meat","cooked_meat"], "chance":0.00004 },
|
|
"plague": { "elem1":"plague", "chance":0.000003 },
|
|
},
|
|
related: ["nothing_there_phase_2", "nothing_there_phase_3_body", "nothing_there_phase_3_head"],
|
|
properties: {
|
|
dead: false,
|
|
dir: 1,
|
|
following: false,
|
|
},
|
|
movable: true,
|
|
tick: function(pixel) {
|
|
var pixelBreakInto = elements[pixel.element].breakInto;
|
|
tryMove(pixel, pixel.x, pixel.y+1); // Fall
|
|
doHeat(pixel);
|
|
doBurning(pixel);
|
|
doElectricity(pixel);
|
|
if (pixel.dead) {
|
|
// Break if pixelTicks-dead > 5
|
|
if (pixelTicks-pixel.dead > 5) {
|
|
changePixel(pixel,pixelBreakInto[Math.floor(Math.random() * pixelBreakInto.length)],false);
|
|
};
|
|
return;
|
|
};
|
|
|
|
if (Math.random() < 0.1) { // Move 10% chance
|
|
var movesToTry = [
|
|
[1*pixel.dir,0], //dash move
|
|
[1*pixel.dir,-1], //cleave move
|
|
];
|
|
// 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(tryMove(pixel, pixel.x+move[0], pixel.y+move[1])) {
|
|
break;
|
|
} else { //move through given pixels
|
|
if(!isEmpty(pixel.x+move[0], pixel.y+move[1], true)) {
|
|
var blockingPixel = pixelMap[pixel.x+move[0]][pixel.y+move[1]];
|
|
//console.log(blockingPixel);
|
|
var blockingElement = blockingPixel.element;
|
|
if(testSwapArray.includes(blockingElement)) {
|
|
swapPixels(pixel,blockingPixel);
|
|
break;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
// 15% chance to change direction while not chasing a human
|
|
if(!pixel.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*");
|
|
};*/
|
|
};
|
|
|
|
var pX = pixel.x;
|
|
var pY = pixel.y;
|
|
|
|
if(Math.random() < 0.01) { //1% chance each tick to lose interest
|
|
pixel.following = false;
|
|
//console.log("Meh.");
|
|
};
|
|
|
|
//Human detection loop (looks ahead according to direction and sets the "following" variable to true, telling the body to lock the direction)
|
|
if(pixelTicks % 2 == 0 && !pixel.dead) { //reduce rate for performance
|
|
/*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 > (-35 - 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) { //If not dead
|
|
pixel.following = true;
|
|
//console.log(`Human detected at (${nX},${nY})`)
|
|
//Infect/kill if a human is close enough
|
|
if(coordPyth(pX,pY,nX,nY) <= 1.5) { //approx. sqrt(2)
|
|
if(Math.random() < 1/4) { //One-fourth chance to change to blood
|
|
changePixel(newPixel,"blood",false);
|
|
} else { //Remaining 3/4 chance to change to meat
|
|
changePixel(newPixel,"meat",false);
|
|
};
|
|
};
|
|
} else { //Mutilate if dead
|
|
if(Math.random() < 1/4) { //One-fourth chance to change to blood
|
|
changePixel(newPixel,"blood",false);
|
|
} else { //Remaining 3/4 chance to change to meat
|
|
changePixel(newPixel,"meat",false);
|
|
};
|
|
};
|
|
} 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 < 35 + 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) { //If not dead
|
|
pixel.following = true;
|
|
//console.log(`Human detected at (${nX},${nY})`)
|
|
//Infect/kill if a human is close enough
|
|
if(coordPyth(pX,pY,nX,nY) <= 1.5) { //approx. sqrt(2)
|
|
if(Math.random() < 1/4) { //One-fourth chance to change to blood
|
|
changePixel(newPixel,"blood",false);
|
|
} else { //Remaining 3/4 chance to change to meat
|
|
changePixel(newPixel,"meat",false);
|
|
};
|
|
};
|
|
} else { //Mutilate if dead
|
|
if(Math.random() < 1/4) { //One-fourth chance to change to blood
|
|
changePixel(newPixel,"blood",false);
|
|
} else { //Remaining 3/4 chance to change to meat
|
|
changePixel(newPixel,"meat",false);
|
|
};
|
|
};
|
|
} else {
|
|
//console.log(`Stopping row look at pixel (${nX},${nY}) due to non-human pixel in the way`)
|
|
break; //can't see through humans
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
if(pixelTicks - pixel.start > 300 && (Math.random() < 0.003)) {
|
|
var dir = pixel.dir;
|
|
changePixel(pixel,"nothing_there_phase_2",false);
|
|
pixel.dir = dir;
|
|
};
|
|
|
|
//End
|
|
},
|
|
};
|
|
|
|
elements.nothing_there_phase_2 = {
|
|
behavior: behaviors.POWDER_OLD,
|
|
color: "#d90b0b",
|
|
category: "life",
|
|
density: 4000,
|
|
desc: "O-06-20 <span style='color: red;'>(ALEPH)</span><br/>In this phase, it looks like a red, fibrous cocoon. It will soon hatch into its third phase.",
|
|
state: "solid",
|
|
tempHigh: 3500,
|
|
hardness: 0.999,
|
|
stateHigh: "cooked_meat",
|
|
burn: 1,
|
|
burnTime: 350000,
|
|
burnInto: "cooked_meat",
|
|
breakInto: (enabledMods.includes("mods/fey_and_more.js") ? ["blood","meat","magic"] : ["blood","meat"]),
|
|
reactions: {
|
|
"cancer": { "elem1":"cancer", "chance":0.000001 },
|
|
"radiation": { "elem1":["ash","meat","rotten_meat","cooked_meat"], "chance":0.000001 },
|
|
"plague": { "elem1":"plague", "chance":0.000001 },
|
|
},
|
|
related: ["nothing_there_phase_1", "nothing_there_phase_3_body", "nothing_there_phase_3_head"],
|
|
properties: {
|
|
dead: false,
|
|
dir: 1,
|
|
timer: 0,
|
|
},
|
|
movable: true,
|
|
tick: function(pixel) {
|
|
var pixelBreakInto = elements[pixel.element].breakInto;
|
|
doHeat(pixel);
|
|
doBurning(pixel);
|
|
doElectricity(pixel);
|
|
if (pixel.dead) {
|
|
// Break if pixelTicks-dead > 5
|
|
if (pixelTicks-pixel.dead > 5) {
|
|
changePixel(pixel,pixelBreakInto[Math.floor(Math.random() * pixelBreakInto.length)],false);
|
|
};
|
|
return;
|
|
};
|
|
|
|
if(pixelTicks - pixel.start > 300) {
|
|
var dir = pixel.dir;
|
|
if (isEmpty(pixel.x, pixel.y+1)) {
|
|
createPixel("nothing_there_phase_3_body", pixel.x, pixel.y+1);
|
|
pixel.element = "nothing_there_phase_3_head";
|
|
pixel.color = pixelColorPick(pixel)
|
|
pixelMap[pixel.x][pixel.y+1].dir = dir;
|
|
}
|
|
else if (isEmpty(pixel.x, pixel.y-1)) {
|
|
createPixel("nothing_there_phase_3_head", pixel.x, pixel.y-1);
|
|
pixelMap[pixel.x][pixel.y-1].color = pixel.color;
|
|
pixel.element = "nothing_there_phase_3_body";
|
|
pixel.color = pixelColorPick(pixel)
|
|
pixel.dir = dir;
|
|
};
|
|
};
|
|
|
|
//End
|
|
},
|
|
};
|
|
|
|
elements.nothing_there_phase_3 = {
|
|
color: "#fc1e35",
|
|
category: "life",
|
|
desc: "Spawns Nothing There in its humanoid third phase, for when you don't want to wait for it to go through the other phases.",
|
|
properties: {
|
|
dead: false,
|
|
dir: 1,
|
|
panic: 0,
|
|
following: false,
|
|
},
|
|
movable: true,
|
|
tick: function(pixel) {
|
|
if (isEmpty(pixel.x, pixel.y+1)) {
|
|
createPixel("nothing_there_phase_3_body", pixel.x, pixel.y+1);
|
|
pixel.element = "nothing_there_phase_3_head";
|
|
pixel.color = pixelColorPick(pixel)
|
|
} else if (isEmpty(pixel.x, pixel.y-1)) {
|
|
createPixel("nothing_there_phase_3_head", pixel.x, pixel.y-1);
|
|
pixel.element = "nothing_there_phase_3_body";
|
|
pixel.color = pixelColorPick(pixel)
|
|
} else {
|
|
deletePixel(pixel.x, pixel.y);
|
|
}
|
|
},
|
|
related: ["nothing_there_phase_3_body","nothing_there_phase_3_head"],
|
|
};
|
|
|
|
elements.nothing_there_phase_3_body = {
|
|
color: "#fc1e35",
|
|
category: "life",
|
|
density: 3000,
|
|
desc: "O-06-20 <span style='color: red;'>(ALEPH)</span><br/>In this phase, it looks like a humanoid made of misarranged flesh. It is almost indestructible and has a variety of ways to destroy your canvas and annihilate any humans inside of it.<br/>Let's hope it doesn't learn to blend in and walk among us.",
|
|
state: "solid",
|
|
tempHigh: 3000,
|
|
hardness: 0.9975,
|
|
hidden: true,
|
|
stateHigh: "cooked_meat",
|
|
burn: 1,
|
|
burnTime: 300000,
|
|
burnInto: "cooked_meat",
|
|
breakInto: (enabledMods.includes("mods/fey_and_more.js") ? ["blood","meat","magic"] : ["blood","meat"]),
|
|
reactions: {
|
|
"cancer": { "elem1":"cancer", "chance":0.00001 },
|
|
"radiation": { "elem1":["ash","meat","rotten_meat","cooked_meat"], "chance":0.00002 },
|
|
"plague": { "elem1":"plague", "chance":0.0000015 },
|
|
},
|
|
properties: {
|
|
dead: false,
|
|
dir: 1,
|
|
panic: 0,
|
|
following: false,
|
|
},
|
|
movable: true,
|
|
related: ["nothing_there_phase_1", "nothing_there_phase_2", "nothing_there_mace", "nothing_there_cleaver", "nothing_there_bullet"],
|
|
tick: function(pixel) {
|
|
var pixelBreakInto = elements[pixel.element].breakInto;
|
|
|
|
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 == "nothing_there_phase_3_head") {
|
|
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) {
|
|
// Break if pixelTicks-dead > 5
|
|
if (pixelTicks-pixel.dead > 5) {
|
|
changePixel(pixel,pixelBreakInto[Math.floor(Math.random() * pixelBreakInto.length)],false);
|
|
};
|
|
return;
|
|
};
|
|
|
|
// Find the head
|
|
if (!isEmpty(pixel.x, pixel.y-1, true)) {
|
|
if(pixelMap[pixel.x][pixel.y-1].element == "nothing_there_phase_3_head") {
|
|
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;
|
|
};
|
|
} else { var head = null };
|
|
|
|
if (isEmpty(pixel.x, pixel.y-1)) {
|
|
// create blood if decapitated 30% chance
|
|
if (Math.random() < 0.3) {
|
|
createPixel("blood", pixel.x, pixel.y-1);
|
|
// set dead to true 10% chance
|
|
if (Math.random() < 0.1) {
|
|
pixel.dead = pixelTicks;
|
|
}
|
|
}
|
|
}
|
|
else if (head == null) { return } //do not proceed if headless
|
|
else if (Math.random() < 0.08) { // 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*");
|
|
};*/
|
|
};
|
|
},
|
|
};
|
|
|
|
elements.nothing_there_phase_3_head = {
|
|
color: "#ff3046",
|
|
category: "life",
|
|
density: 3000,
|
|
desc: "O-06-20 <span style='color: red;'>(ALEPH)</span><br/>In this phase, it looks like a humanoid made of misarranged flesh. It is almost indestructible and has a variety of ways to destroy your canvas and annihilate any humans inside of it.<br/>Let's hope it doesn't learn to blend in and walk among us.",
|
|
state: "solid",
|
|
tempHigh: 3000,
|
|
hardness: 0.9975,
|
|
hidden: true,
|
|
stateHigh: "cooked_meat",
|
|
burn: 1,
|
|
burnTime: 300000,
|
|
burnInto: "cooked_meat",
|
|
breakInto: (enabledMods.includes("mods/fey_and_more.js") ? ["blood","meat","magic"] : ["blood","meat"]),
|
|
reactions: {
|
|
"cancer": { "elem1":"cancer", "chance":0.00001 },
|
|
"radiation": { "elem1":["ash","meat","rotten_meat","cooked_meat"], "chance":0.00002 },
|
|
"plague": { "elem1":"plague", "chance":0.0000015 },
|
|
},
|
|
properties: {
|
|
dead: false,
|
|
dir: 1,
|
|
panic: 0,
|
|
following: false,
|
|
},
|
|
movable: true,
|
|
related: ["nothing_there_phase_1", "nothing_there_phase_2", "nothing_there_mace", "nothing_there_cleaver", , "nothing_there_bullet"],
|
|
tick: function(pixel) {
|
|
var pixelBreakInto = elements[pixel.element].breakInto;
|
|
doHeat(pixel);
|
|
doBurning(pixel);
|
|
doElectricity(pixel);
|
|
if (pixel.dead) {
|
|
// Break if pixelTicks-dead > 5
|
|
if (pixelTicks-pixel.dead > 5) {
|
|
changePixel(pixel,pixelBreakInto[Math.floor(Math.random() * pixelBreakInto.length)],false);
|
|
};
|
|
return;
|
|
};
|
|
|
|
// Find the body
|
|
if (!isEmpty(pixel.x, pixel.y+1, true)) {
|
|
if(pixelMap[pixel.x][pixel.y+1].element == "nothing_there_phase_3_body") {
|
|
var body = pixelMap[pixel.x][pixel.y+1];
|
|
if (body.dead) { // If body is dead, kill body
|
|
pixel.dead = body.dead;
|
|
};
|
|
} else {
|
|
var body = null;
|
|
};
|
|
} 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 30% chance
|
|
if (isEmpty(pixel.x, pixel.y+1) && Math.random() < 0.3) {
|
|
createPixel("blood", pixel.x, pixel.y+1);
|
|
// set dead to true 10% chance
|
|
if (Math.random() < 0.10) {
|
|
pixel.dead = pixelTicks;
|
|
}
|
|
}
|
|
}
|
|
|
|
//start of most new code
|
|
var pX = pixel.x;
|
|
var pY = pixel.y;
|
|
|
|
//Human detection loop
|
|
if(pixelTicks % 2 == 0 && !pixel.dead) { //reduce rate for performance
|
|
/*var directionAdverb = "left";
|
|
if(pixel.dir > 0) {
|
|
directionAdverb = "right";
|
|
};*/
|
|
//console.log(`Looking ${directionAdverb}`)
|
|
if(pixel.dir === -1) {
|
|
//do action every 40 ticks
|
|
var bulletPositions = [[-1, -1], [-1, 0]];
|
|
var bulletPosition = bulletPositions[Math.floor(Math.random() * 2)];
|
|
|
|
var smashPosition = [-1, -1];
|
|
|
|
var cleavePositions = [[-1, -1], [-2, -1], [-3, -1]];
|
|
|
|
var start = 2 * Math.floor(pixel.start/2);
|
|
if((pixelTicks - start) % 40 == 0) {
|
|
var action = Math.floor(Math.random() * 3);
|
|
if(action == 0) { //bullet
|
|
var bX = pX + bulletPosition[0];
|
|
var bY = pY + bulletPosition[1];
|
|
|
|
if(!outOfBounds(bX,bY)) {
|
|
if(isEmpty(bX,bY)) {
|
|
createPixel("nothing_there_bullet",bX,bY);
|
|
pixelMap[bX][bY].flipX = true;
|
|
} else {
|
|
if(!nothingThereBulletExcludedElements.includes(pixelMap[bX][bY].element)) {
|
|
deletePixel(bX,bY);
|
|
createPixel("nothing_there_bullet",bX,bY);
|
|
pixelMap[bX][bY].flipX = true;
|
|
};
|
|
};
|
|
};
|
|
} else if(action == 1) { //smash
|
|
var sX = pX + smashPosition[0];
|
|
var sY = pY + smashPosition[1];
|
|
|
|
if(!outOfBounds(sX,sY)) {
|
|
if(isEmpty(sX,sY)) {
|
|
createPixel("nothing_there_mace",sX,sY);
|
|
} else {
|
|
if(!nothingThereBulletExcludedElements.includes(pixelMap[sX][sY].element)) {
|
|
deletePixel(sX,sY);
|
|
createPixel("nothing_there_mace",sX,sY);
|
|
};
|
|
};
|
|
};
|
|
} else if(action == 2) { //cleave
|
|
for(cleaverIndex = 0; cleaverIndex < cleavePositions.length; cleaverIndex++) {
|
|
var cX = pX + cleavePositions[cleaverIndex][0];
|
|
var cY = pY + cleavePositions[cleaverIndex][1];
|
|
|
|
if(!outOfBounds(cX,cY)) {
|
|
if(isEmpty(cX,cY)) {
|
|
createPixel("nothing_there_cleaver",cX,cY);
|
|
} else {
|
|
if(!nothingThereBulletExcludedElements.includes(pixelMap[cX][cY].element)) {
|
|
deletePixel(cX,cY);
|
|
createPixel("nothing_there_cleaver",cX,cY);
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
for(i = -4; i < 4+1; i++) {
|
|
var oY = i;
|
|
//console.log(`Starting row look at row ${pY+oY}`)
|
|
for(j = (-1); j > (-35 - 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) { //If not dead
|
|
pixel.following = true;
|
|
//console.log(`Human detected at (${nX},${nY})`)
|
|
//Infect/kill if a human is close enough
|
|
if(coordPyth(pX,pY,nX,nY) <= 1.5) { //approx. sqrt(2)
|
|
if(Math.random() < 1/4) { //One-fourth chance to change to blood
|
|
changePixel(newPixel,"blood",false);
|
|
} else { //Remaining 3/4 chance to change to meat
|
|
changePixel(newPixel,"meat",false);
|
|
};
|
|
};
|
|
} else { //Mutilate if dead
|
|
if(Math.random() < 1/4) { //One-fourth chance to change to blood
|
|
changePixel(newPixel,"blood",false);
|
|
} else { //Remaining 3/4 chance to change to meat
|
|
changePixel(newPixel,"meat",false);
|
|
};
|
|
};
|
|
} 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) {
|
|
//do action every 40 ticks
|
|
var bulletPositions = [[1, -1], [1, 0]];
|
|
var bulletPosition = bulletPositions[Math.floor(Math.random() * 2)];
|
|
|
|
var smashPosition = [1, -1];
|
|
|
|
var cleavePositions = [[1, -1], [2, -1], [3, -1]];
|
|
|
|
var start = 2 * Math.floor(pixel.start/2);
|
|
if((pixelTicks - start) % 40 == 0) {
|
|
var action = Math.floor(Math.random() * 3);
|
|
if(action == 0) { //bullet
|
|
var bX = pX + bulletPosition[0];
|
|
var bY = pY + bulletPosition[1];
|
|
|
|
if(!outOfBounds(bX,bY)) {
|
|
if(isEmpty(bX,bY)) {
|
|
createPixel("nothing_there_bullet",bX,bY);
|
|
pixelMap[bX][bY].flipX = false;
|
|
} else {
|
|
if(!nothingThereBulletExcludedElements.includes(pixelMap[bX][bY].element)) {
|
|
deletePixel(bX,bY);
|
|
createPixel("nothing_there_bullet",bX,bY);
|
|
pixelMap[bX][bY].flipX = false;
|
|
};
|
|
};
|
|
};
|
|
} else if(action == 1) { //smash
|
|
var sX = pX + smashPosition[0];
|
|
var sY = pY + smashPosition[1];
|
|
|
|
if(!outOfBounds(sX,sY)) {
|
|
if(isEmpty(sX,sY)) {
|
|
createPixel("nothing_there_mace",sX,sY);
|
|
} else {
|
|
if(!nothingThereBulletExcludedElements.includes(pixelMap[sX][sY].element)) {
|
|
deletePixel(sX,sY);
|
|
createPixel("nothing_there_mace",sX,sY);
|
|
};
|
|
};
|
|
};
|
|
} else if(action == 2) { //cleave
|
|
for(cleaverIndex = 0; cleaverIndex < cleavePositions.length; cleaverIndex++) {
|
|
var cX = pX + cleavePositions[cleaverIndex][0];
|
|
var cY = pY + cleavePositions[cleaverIndex][1];
|
|
|
|
if(!outOfBounds(cX,cY)) {
|
|
if(isEmpty(cX,cY)) {
|
|
createPixel("nothing_there_cleaver",cX,cY);
|
|
} else {
|
|
if(!nothingThereBulletExcludedElements.includes(pixelMap[cX][cY].element)) {
|
|
deletePixel(cX,cY);
|
|
createPixel("nothing_there_cleaver",cX,cY);
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
for(i = -4; i < 4+1; i++) {
|
|
var oY = i;
|
|
//console.log(`Starting row look at row ${pY+oY}`)
|
|
for(j = 1; j < 35 + 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) { //If not dead
|
|
pixel.following = true;
|
|
//console.log(`Human detected at (${nX},${nY})`)
|
|
//Infect/kill if a human is close enough
|
|
if(coordPyth(pX,pY,nX,nY) <= 1.5) { //approx. sqrt(2)
|
|
if(Math.random() < 1/4) { //One-fourth chance to change to blood
|
|
changePixel(newPixel,"blood",false);
|
|
} else { //Remaining 3/4 chance to change to meat
|
|
changePixel(newPixel,"meat",false);
|
|
};
|
|
};
|
|
} else { //Mutilate if dead
|
|
if(Math.random() < 1/4) { //One-fourth chance to change to blood
|
|
changePixel(newPixel,"blood",false);
|
|
} else { //Remaining 3/4 chance to change to meat
|
|
changePixel(newPixel,"meat",false);
|
|
};
|
|
};
|
|
} else {
|
|
//console.log(`Stopping row look at pixel (${nX},${nY}) due to non-human pixel in the way`)
|
|
break; //can't see through humans
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
if(Math.random() < 0.01) { //1% chance each tick to lose interest
|
|
pixel.following = false;
|
|
//console.log("Meh.");
|
|
};
|
|
},
|
|
};
|
|
|
|
runAfterLoad(function() {
|
|
if(typeof(badPixels) === "object") {
|
|
badPixels.nothing_there_phase_1 = { panicIncrease: 1, panicIncreaseChance: 1 } //insta-panic for "aleph" thing and "level 1" humans
|
|
badPixels.nothing_there_phase_2 = { panicIncrease: 1, panicIncreaseChance: 1 }
|
|
badPixels.nothing_there_phase_3_body = { panicIncrease: 1, panicIncreaseChance: 1 }
|
|
badPixels.nothing_there_phase_3_head = { panicIncrease: 1, panicIncreaseChance: 1 }
|
|
}
|
|
});
|
|
|
|
/* +-----------------------------------+
|
|
| End Nothing There elements |
|
|
| |
|
|
| |
|
|
| |
|
|
| |
|
|
| |
|
|
+-----------------------------------+ */
|
|
|
|
/* +++++++++++++++++++++++++++
|
|
+ Start skeleton elements +
|
|
+++++++++++++++++++++++++++ */
|
|
|
|
arrowExcludedElements = ["wall"];
|
|
|
|
arrowPlacementExcludedElements = ["skeleton_head", "skeleton_body", "arrow", "wall"];
|
|
|
|
elements.rock.hardness = 0.55;
|
|
|
|
elements.arrow = {
|
|
cooldown: 2,
|
|
flippableX: true,
|
|
movable: true,
|
|
properties: {
|
|
flipY: false,
|
|
speed: 5,
|
|
fall: 0,
|
|
attached: false,
|
|
attachOffsets: [null, null],
|
|
penetrateCounter: 7,
|
|
},
|
|
density: 2471,
|
|
color: "#cacdcf",
|
|
related: ["skeleton_body","skeleton_head"],
|
|
movable: true,
|
|
burn: 20,
|
|
//burnInto: "flint",
|
|
burnInto: "rock",
|
|
burnTime: 250,
|
|
breakInto: ["gravel","gravel","sawdust","feather"],
|
|
tick: function(pixel) {
|
|
if(pixel.attachOffsets.includes(null)) {
|
|
pixel.attached = false;
|
|
};
|
|
if(pixel.attached) {
|
|
var attachCoords = [pixel.x+pixel.attachOffsets[0], pixel.y+pixel.attachOffsets[1]];
|
|
var attachX = pixel.x + pixel.attachOffsets[0];
|
|
var attachY = pixel.y + pixel.attachOffsets[1];
|
|
if(isEmpty(attachX,attachY,true)) {
|
|
pixel.attached = false;
|
|
} else {
|
|
var attachPixel = pixelMap[attachX][attachY];
|
|
var attachInfo = elements[attachPixel.element];
|
|
var attachState = "solid";
|
|
if(typeof(attachInfo.state) === "string") {
|
|
attachState = attachInfo.state;
|
|
};
|
|
var attachBlacklistStates = ["liquid","gas"];
|
|
if(attachBlacklistStates.includes(attachState)) {
|
|
pixel.attached = false;
|
|
};
|
|
};
|
|
} else { //Move if not attached
|
|
var speedForBreakMult = pythSpeed(pixel.speed,pixel.y);
|
|
var breakMult = speedForBreakMult/5;
|
|
|
|
if(typeof(pixel.flipX) == undefined) {
|
|
pixel.flipX = !!Math.floor(Math.random() * 2);
|
|
};
|
|
var dir = pixel.flipX ? -1 : 1;
|
|
if(Math.random() < (1/(pixel.speed**1.585))) { //1/0 is Infinity in JavaScript, so this should always be true at 0 speed)
|
|
pixel.fall++;
|
|
};
|
|
//Horizontal movement
|
|
for(i = 0; i < pixel.speed; i++) {
|
|
if(outOfBounds(pixel.x+dir,pixel.y)) {
|
|
deletePixel(pixel.x,pixel.y);
|
|
break;
|
|
};
|
|
if(!isEmpty(pixel.x+dir,pixel.y,true)) {
|
|
var otherPixel = pixelMap[pixel.x+dir][pixel.y];
|
|
var otherElement = otherPixel.element;
|
|
var otherInfo = elements[otherElement];
|
|
if(arrowExcludedElements.includes(otherElement)) {
|
|
pixel.attached = true; //attach
|
|
pixel.speed = 0;
|
|
pixel.fall = 0;
|
|
pixel.attachOffsets = [dir, 0];
|
|
break;
|
|
};
|
|
var otherDensity = (typeof(otherInfo.density) === "undefined" ? 1000 : otherInfo.density);
|
|
var swapChance = 1 - Math.max(0,(otherDensity / 2471));
|
|
if(Math.random() < swapChance && pixel.penetrateCounter > 0) {
|
|
swapPixels(pixel,otherPixel);
|
|
arrowAltTb(otherPixel,breakMult);
|
|
pixel.speed = Math.max(0,--pixel.speed);
|
|
pixel.penetrateCounter--;
|
|
} else {
|
|
if(!arrowAltTb(otherPixel,breakMult)) { //if this didn't break it
|
|
pixel.attached = true; //attach
|
|
pixel.speed = 0;
|
|
pixel.fall = 0;
|
|
pixel.attachOffsets = [dir, 0];
|
|
};
|
|
};
|
|
break;
|
|
} else {
|
|
tryMove(pixel,pixel.x+dir,pixel.y);
|
|
};
|
|
};
|
|
if(Math.random() < 0.1) {
|
|
pixel.speed = Math.max(0,--pixel.speed);
|
|
};
|
|
|
|
var dirY = 1;
|
|
//Vertical movement
|
|
if(typeof(pixel.flipY) !== "undefined") {
|
|
if(pixel.flipY) {
|
|
pixel.dirY = -1;
|
|
};
|
|
};
|
|
|
|
for(j = 0; j < pixel.fall; j++) {
|
|
if(outOfBounds(pixel.x,pixel.y+dirY)) {
|
|
deletePixel(pixel.x,pixel.y);
|
|
break;
|
|
};
|
|
if(!isEmpty(pixel.x,pixel.y+dirY,true)) {
|
|
var otherPixel = pixelMap[pixel.x][pixel.y+dirY];
|
|
var otherElement = otherPixel.element;
|
|
var otherInfo = elements[otherElement];
|
|
if(arrowExcludedElements.includes(otherElement)) {
|
|
pixel.attached = true; //attach
|
|
pixel.speed = 0;
|
|
pixel.fall = 0;
|
|
pixel.attachOffsets = [0, dirY];
|
|
break;
|
|
};
|
|
var otherDensity = (typeof(otherInfo.density) === "undefined" ? 1000 : otherInfo.density);
|
|
var swapChance = 1 - Math.max(0,(otherDensity / 2471));
|
|
if(Math.random() < swapChance && pixel.penetrateCounter > 0) {
|
|
swapPixels(pixel,otherPixel);
|
|
arrowAltTb(otherPixel,breakMult);
|
|
pixel.speed = Math.max(0,--pixel.speed);
|
|
pixel.penetrateCounter--;
|
|
} else {
|
|
if(!arrowAltTb(otherPixel,breakMult)) { //if this didn't break it
|
|
pixel.attached = true; //attach
|
|
pixel.speed = 0;
|
|
pixel.fall = 0;
|
|
pixel.attachOffsets = [0, dirY];
|
|
};
|
|
};
|
|
break;
|
|
} else {
|
|
tryMove(pixel,pixel.x,pixel.y+dirY);
|
|
};
|
|
};
|
|
|
|
//End
|
|
};
|
|
},
|
|
};
|
|
|
|
elements.skeleton = {
|
|
color: ["#ebebe6", "#cfcfc8"],
|
|
category: "life",
|
|
properties: {
|
|
dead: false,
|
|
dir: 1,
|
|
panic: 0,
|
|
following: false,
|
|
},
|
|
movable: true,
|
|
tick: function(pixel) {
|
|
if (isEmpty(pixel.x, pixel.y+1)) {
|
|
createPixel("skeleton_body", pixel.x, pixel.y+1);
|
|
pixel.element = "skeleton_head";
|
|
pixel.color = pixelColorPick(pixel)
|
|
}
|
|
else if (isEmpty(pixel.x, pixel.y-1)) {
|
|
createPixel("skeleton_head", pixel.x, pixel.y-1);
|
|
pixelMap[pixel.x][pixel.y-1].color = pixel.color;
|
|
pixel.element = "skeleton_body";
|
|
pixel.color = pixelColorPick(pixel)
|
|
}
|
|
else {
|
|
deletePixel(pixel.x, pixel.y);
|
|
}
|
|
},
|
|
related: ["skeleton_body","skeleton_head"],
|
|
desc: "<span class=\"skeletonStatus\">If this text is green or underlined, skeletons can spawn.</span> <span onclick=toggleSkeletonSpawning() style=\"color: #ff00ff;\";>Click here</span> to toggle skeleton spawning. If it's on, skeletons (all types) can spawn through random events."
|
|
};
|
|
|
|
elements.skeleton_body = {
|
|
color: "#ebebe6",
|
|
category: "life",
|
|
hidden: true,
|
|
density: 1500,
|
|
state: "solid",
|
|
conduct: 25,
|
|
tempHigh: 250,
|
|
stateHigh: "bone",
|
|
burn: 10,
|
|
burnTime: 250,
|
|
burnInto: ["bone","ash","arrow"],
|
|
hardness: 0.55,
|
|
breakInto: ["bone","bone","bone","bone_marrow"],
|
|
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,
|
|
chargeCounter: 20,
|
|
shooting: false,
|
|
},
|
|
movable: true,
|
|
tick: function(pixel) {
|
|
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 == "skeleton_head") {
|
|
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 bone if pixelTicks-dead > 500
|
|
if (pixelTicks-pixel.dead > 200) {
|
|
changePixel(pixel,"bone");
|
|
}
|
|
return
|
|
}
|
|
|
|
// Find the head
|
|
if (!isEmpty(pixel.x, pixel.y-1, true) && pixelMap[pixel.x][pixel.y-1].element == "skeleton_head") {
|
|
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 (bone marrow)
|
|
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.shooting) {
|
|
if(pixel.chargeCounter <= 0) {
|
|
var bX = pixel.x + pixel.dir;
|
|
var bY = pixel.y - 1;
|
|
var arrowFlipX = null;
|
|
if(pixel.dir < 0) {
|
|
arrowFlipX = true;
|
|
} else if(pixel.dir > 0) {
|
|
arrowFlipX = false;
|
|
};
|
|
if(!outOfBounds(bX,bY)) {
|
|
if(isEmpty(bX,bY)) {
|
|
createPixel("arrow",bX,bY);
|
|
pixelMap[bX][bY].flipX = arrowFlipX;
|
|
} else {
|
|
if(!arrowExcludedElements.includes(pixelMap[bX][bY].element) && !arrowPlacementExcludedElements.includes(pixelMap[bX][bY].element)) {
|
|
deletePixel(bX,bY);
|
|
createPixel("arrow",bX,bY);
|
|
pixelMap[bX][bY].flipX = arrowFlipX;
|
|
};
|
|
};
|
|
};
|
|
pixel.chargeCounter = 20;
|
|
};
|
|
if(pixel.chargeCounter > 0) {
|
|
pixel.chargeCounter--;
|
|
};
|
|
};
|
|
},
|
|
};
|
|
|
|
elements.skeleton_head = {
|
|
color: ["#ebebe6", "#cfcfc8"],
|
|
category: "life",
|
|
hidden: true,
|
|
density: 1500,
|
|
state: "solid",
|
|
conduct: 25,
|
|
tempHigh: 250,
|
|
stateHigh: "bone",
|
|
burn: 10,
|
|
burnTime: 250,
|
|
burnInto: ["bone","ash","arrow"],
|
|
hardness: 0.55,
|
|
breakInto: ["bone","bone","bone","bone_marrow"],
|
|
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,
|
|
},
|
|
movable: true,
|
|
tick: function(pixel) {
|
|
doHeat(pixel);
|
|
doBurning(pixel);
|
|
doElectricity(pixel);
|
|
if (pixel.dead) {
|
|
// Turn into bone if pixelTicks-dead > 500
|
|
if (pixelTicks-pixel.dead > 200) {
|
|
changePixel(pixel,"bone");
|
|
}
|
|
return
|
|
}
|
|
|
|
// Find the body
|
|
if (!isEmpty(pixel.x, pixel.y+1, true) && pixelMap[pixel.x][pixel.y+1].element == "skeleton_body") {
|
|
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;
|
|
|
|
//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;
|
|
if(body) body.shooting = true;
|
|
};
|
|
} 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;
|
|
if(body) body.shooting = true;
|
|
};
|
|
} else {
|
|
//console.log(`Stopping row look at pixel (${nX},${nY}) due to non-human pixel in the way`)
|
|
break;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
if(Math.random() < 0.01) { //1% chance each tick to lose interest
|
|
pixel.following = false;
|
|
//console.log("Meh.");
|
|
};
|
|
},
|
|
};
|
|
|
|
/* -------------------------
|
|
- End skeleton elements -
|
|
------------------------- */
|
|
mobsLoaded = true;
|
|
});
|
|
} 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) };
|
|
alert(`The "${runAfterAutogenMod}", "${libraryMod}", and "${explodeAtPlusMod}" mods are all required; any missing mods in this list have been automatically inserted (reload for this to take effect).`)
|
|
localStorage.setItem("enabledMods", JSON.stringify(enabledMods));
|
|
};
|