From a123fb2fafce7af2e023967145bb9b84e631af50 Mon Sep 17 00:00:00 2001 From: O-01-67 <68935009+O-01-67@users.noreply.github.com> Date: Thu, 6 Oct 2022 14:15:16 -0400 Subject: [PATCH] Add zombies and baby zombies, rename to mobs.js --- mods/creepers.js => mobs.js | 825 ++++++++++++++++++++++++++++++++++++ 1 file changed, 825 insertions(+) rename mods/creepers.js => mobs.js (78%) diff --git a/mods/creepers.js b/mobs.js similarity index 78% rename from mods/creepers.js rename to mobs.js index 2d98bf69..d101f713 100644 --- a/mods/creepers.js +++ b/mobs.js @@ -1,5 +1,14 @@ //Prerequisite Functions and Variables +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", +}; + var style = document.createElement('style'); //Initialize CSS for creeper spawning's status indicator style.type = 'text/css'; style.id = 'creeperStatusStylesheet'; @@ -286,6 +295,820 @@ standaloneSpawnCreeper = function(amount=1) { }; }; +//Prerequisite Functions and Variables + +function getKeyByValue(object, value) { + return Object.keys(object).find(key => object[key] === value); +} +//getKeyByValue code by UncleLaz on StackOverflow: https://stackoverflow.com/questions/9907419/how-to-get-a-key-in-a-javascript-object-by-its-value" + +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.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() * 3)+1); //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() * 3)+1); //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); + }; + }; +}; + +/*Start Main Zombie + .................. + .........###...... + .......#######.... + ......#####OOOOO.. + ....######OOOOOOO. + ...########OOOOO.. + ...###########.... + ...###########.... + ...###########.... + ....##########.... + ......########.... + .......##...##.... + .......##...##.... + .................. +*/ + +elements.zombie = { + color: ["#567C44","#199A9A","#41369B"], + category: "life", + properties: { + dead: false, + dir: 1, + panic: 0, + following: false, + }, + 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: "I'd rather this be toggleable mid-game than require a reload.

If this text is green or underlined, zombies (all types) can spawn. Click here 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, + }, + 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, + }, + 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(pyth(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(pyth(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, + }, + 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(pyth(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(pyth(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 ################## ######### ###### @@ -3352,6 +4175,8 @@ runAfterLoad(function() { } else { //console.log("nyetted " + placerName); }; + + headBodyObject[headName] = bodyName; }; });