subway gates

subway
  start in an empty station
  station exits are blocked by a gate that opens when you press a button
  level exit spawns after you clear 4 stations
  added 2 new stations (7 possible stations)

laser-bots lasers wiggle as it aims at the target
  this is a nerf to iridescence and it looks cool

bug fixes
This commit is contained in:
landgreen
2023-06-24 11:34:47 -07:00
parent 1d4b0c4430
commit 4415942b94
6 changed files with 485 additions and 138 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

@@ -5500,21 +5500,10 @@ const b = {
const push = 0.4
const reflectivity = 1 - 1 / (tech.laserReflections * 3)
let damage = m.dmgScale * this.laserDamage * tech.laserDamage
let best = {
x: 1,
y: 1,
dist2: Infinity,
who: null,
v1: 1,
v2: 1
};
const path = [{
x: this.vertices[0].x,
y: this.vertices[0].y
}, {
x: this.lockedOn.position.x,
y: this.lockedOn.position.y
}];
//make the laser wiggle as it aims at the target
let best = { x: 1, y: 1, dist2: Infinity, who: null, v1: 1, v2: 1 };
const perp2 = Vector.mult(Vector.rotate({ x: 1, y: 0 }, m.angle + Math.PI / 2), 0.6 * this.lockedOn.radius * Math.sin(simulation.cycle / this.lookFrequency))
const path = [{ x: this.vertices[0].x, y: this.vertices[0].y }, Vector.add(this.lockedOn.position, perp2)];
const vertexCollision = function (v1, v1End, domain) {
for (let i = 0; i < domain.length; ++i) {

View File

@@ -786,7 +786,7 @@ ${simulation.isCheating ? "<br><br><em>lore disabled</em>" : ""}
<button onclick="build.sortTech('fieldtech', true)" class='sort-button'><strong class='color-f'>field</strong><strong class='color-m'>tech</strong></button>
<button onclick="build.sortTech('damage', true)" class='sort-button'><strong class='color-d'>damage</strong></button>
<button onclick="build.sortTech('defense', true)" class='sort-button'><strong style="letter-spacing: 1px;font-weight: 100;">defense</strong></button>
<button onclick="build.sortTech('have', true)" class='sort-button' style="letter-spacing: 1px;font-weight: 800;">have</button>
<button onclick="build.sortTech('have', true)" class='sort-button color-m' style="letter-spacing: 1px;font-weight: 800;">have</button>
<button onclick="build.sortTech('allowed', true)" class='sort-button' style="letter-spacing: 1px;font-weight: 400;">allowed</button>
<input type="search" id="sort-input" style="width: 8.7em;font-size: 0.6em;color:#000;" placeholder="sort by"/>
<button onclick="build.sortTech('input', true)" class='sort-button' style="border-radius: 0em;border: 1.5px #000 solid;font-size: 0.6em;" value="damage">sort</button>

View File

@@ -34,13 +34,13 @@ const level = {
// b.giveGuns("nail gun") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser
// b.giveGuns("shotgun") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser
// b.guns[3].ammo = 100000000
// tech.giveTech("bremsstrahlung")
// tech.giveTech("iridescence")
// tech.giveTech("cherenkov radiation")
// tech.giveTech("irradiated nails")
// for (let i = 0; i < 6; ++i) tech.giveTech("Lorentz transformation")
// for (let i = 0; i < 1; ++i) tech.giveTech("rivet gun")
// requestAnimationFrame(() => { for (let i = 0; i < 1; i++) tech.giveTech("foam-bot") });
// for (let i = 0; i < 1; i++) tech.giveTech("foam-bot upgrade")
// requestAnimationFrame(() => { for (let i = 0; i < 30; i++) tech.giveTech("laser-bot") });
// for (let i = 0; i < 1; i++) tech.giveTech("laser-bot upgrade")
// for (let i = 0; i < 1; ++i) tech.giveTech("ternary")
// for (let i = 0; i < 3; ++i) tech.giveTech("mechatronics")
// for (let i = 0; i < 3; i++) powerUps.directSpawn(450, -50, "tech");
@@ -485,7 +485,6 @@ const level = {
category: cat.body,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
isNoSetCollision: true,
isNotHoldable: true,
frictionAir: frictionAir,
friction: 1,
@@ -518,7 +517,6 @@ const level = {
category: cat.body,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
isNoSetCollision: true,
isNotHoldable: true,
frictionAir: frictionAir,
friction: 1,
@@ -652,7 +650,6 @@ const level = {
category: cat.body, //cat.map,
mask: cat.map | cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
isNoSetCollision: true,
inertia: Infinity, //prevents rotation
isNotHoldable: true,
friction: 1,
@@ -821,7 +818,6 @@ const level = {
// category: cat.map,
// mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
// },
// isNoSetCollision: true,
// });
// const rotor2 = Matter.Bodies.rectangle(x, y, width, radius, {
// angle: Math.PI / 2,
@@ -832,7 +828,6 @@ const level = {
// category: cat.map,
// mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
// },
// isNoSetCollision: true,
// });
// rotor = Body.create({ //combine rotor1 and rotor2
// parts: [rotor1, rotor2],
@@ -841,7 +836,6 @@ const level = {
// category: cat.map,
// mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
// },
// isNoSetCollision: true,
// });
// Matter.Body.setPosition(rotor, {
// x: x,
@@ -892,7 +886,6 @@ const level = {
const height = 15
body[body.length] = Bodies.rectangle(x + width / 2, y + height / 2, width, height, { friction: 0.05, frictionAir: 0.01 });
let flip = body[body.length - 1];
flip.isNoSetCollision = true //prevents collision from being rewritten in level.addToWorld
flip.collisionFilter.category = cat.body
flip.collisionFilter.mask = cat.player | cat.body
flip.isNotHoldable = true
@@ -948,12 +941,13 @@ const level = {
},
}
},
button(x, y, width = 126) {
button(x, y, width = 126, isSpawnBase = true) {
if (isSpawnBase) {
spawn.mapVertex(x + 65, y + 2, "100 10 -100 10 -70 -10 70 -10");
map[map.length - 1].restitution = 0;
map[map.length - 1].friction = 1;
map[map.length - 1].frictionStatic = 1;
}
// const buttonSensor = Bodies.rectangle(x + 35, y - 1, 70, 20, {
// isSensor: true
// });
@@ -992,6 +986,25 @@ const level = {
this.isUp = false;
}
},
query() {
if (Matter.Query.region(body, this).length === 0 && Matter.Query.region([player], this).length === 0) {
this.isUp = true;
} else {
if (this.isUp === true) {
const list = Matter.Query.region(body, this) //are any blocks colliding with this
if (list.length > 0) {
if (list[0].bounds.max.x - list[0].bounds.min.x < 150 && list[0].bounds.max.y - list[0].bounds.min.y < 150) { //not too big of a block
Matter.Body.setPosition(list[0], { //teleport block to the center of the button
x: this.min.x + width / 2,
y: list[0].position.y
})
}
Matter.Body.setVelocity(list[0], { x: 0, y: 0 });
}
}
this.isUp = false;
}
},
draw() {
ctx.fillStyle = "hsl(0, 100%, 70%)"
if (this.isUp) {
@@ -1035,7 +1048,6 @@ const level = {
category: cat.map,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
isNoSetCollision: true,
inertia: Infinity, //prevents rotation
isNotHoldable: true,
isNonStick: true, //this keep sporangium from sticking
@@ -1187,7 +1199,6 @@ const level = {
category: cat.body,//cat.map,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
isNoSetCollision: true,
inertia: Infinity, //prevents rotation
isNotHoldable: true,
friction: 1,
@@ -1210,25 +1221,6 @@ const level = {
Matter.Body.setPosition(this, position)
}
}
// else {
// const blocks = Matter.Query.collides(this, body)
// console.log(blocks.length)
// for (let i = 0; i < blocks.length; i++) {
// // console.log(blocks[i])
// if (blocks[i].bodyA) {
// if (touching[i].bodyB !== m.holdingTarget) {
// for (let j = 0, len = body.length; j < len; j++) {
// if (body[j] === touching[i].bodyB) {
// body.splice(j, 1);
// len--
// Matter.Composite.remove(engine.world, touching[i].bodyB);
// break;
// }
// }
// }
// }
// }
// }
} else {
if (this.position.y > y - distance) { //try to open
const position = {
@@ -1248,9 +1240,7 @@ const level = {
ctx.beginPath();
const v = this.vertices;
ctx.moveTo(v[0].x, v[0].y);
for (let i = 1; i < v.length; ++i) {
ctx.lineTo(v[i].x, v[i].y);
}
for (let i = 1; i < v.length; ++i) ctx.lineTo(v[i].x, v[i].y);
ctx.lineTo(v[0].x, v[0].y);
ctx.fill();
}
@@ -1260,6 +1250,69 @@ const level = {
doorBlock.classType = "body"
return doorBlock
},
doorMap(x, y, width, height, distance, speed = 20, addToWorld = true) { //for doors that use line of sight
x = x + width / 2
y = y + height / 2
const door = map[map.length] = Bodies.rectangle(x, y, width, height, {
collisionFilter: {
category: cat.map,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet,
// mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
inertia: Infinity, //prevents rotation
isNotHoldable: true,
friction: 1,
frictionStatic: 1,
restitution: 0,
isClosing: false,
openClose(isSetPaths = false) {
if (!m.isBodiesAsleep) {
if (this.isClosing) {
if (this.position.y < y) { //try to close
if ( //if clear of stuff
Matter.Query.collides(this, [player]).length === 0 &&
Matter.Query.collides(this, body).length < 2 &&
Matter.Query.collides(this, mob).length === 0
) {
const position = {
x: this.position.x,
y: this.position.y + speed
}
Matter.Body.setPosition(this, position)
if (isSetPaths) simulation.draw.setPaths()
}
}
} else {
if (this.position.y > y - distance) { //try to open
const position = {
x: this.position.x,
y: this.position.y - speed
}
Matter.Body.setPosition(this, position)
if (isSetPaths) simulation.draw.setPaths()
}
}
}
},
isClosed() {
return this.position.y > y - 1
},
draw() {
ctx.fillStyle = "#666"
ctx.beginPath();
const v = this.vertices;
ctx.moveTo(v[0].x, v[0].y);
for (let i = 1; i < v.length; ++i) ctx.lineTo(v[i].x, v[i].y);
ctx.lineTo(v[0].x, v[0].y);
ctx.fill();
}
});
Matter.Body.setStatic(door, true); //make static
if (addToWorld) Composite.add(engine.world, door); //add to world
door.classType = "map"
return door
},
portal(centerA, angleA, centerB, angleB) {
const width = 50
const height = 150
@@ -1572,7 +1625,6 @@ const level = {
category: cat.map,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
isNoSetCollision: true,
inertia: Infinity, //prevents rotation
isNotHoldable: true,
friction: 0,
@@ -1655,7 +1707,6 @@ const level = {
category: cat.body,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
isNoSetCollision: true,
inertia: Infinity, //prevents rotation
isNotHoldable: true,
friction: 0,
@@ -2534,8 +2585,6 @@ const level = {
// level.difficultyIncrease(10 * 4);
// m.maxHealth = m.health = 100
const mobSpawnCap = mobs.mobDeaths + 100
level.isProcedural = true //used in generating text for the level builder
simulation.draw.drawMapPath = simulation.draw.drawMapSight
@@ -2543,13 +2592,12 @@ const level = {
document.body.style.backgroundColor = "#e3e3e3"//"#e3e3e3"//color.map//"#333"//"#000"
level.defaultZoom = 1400
simulation.zoomTransition(level.defaultZoom)
level.setPosToSpawn(1400 * (Math.random() < 0.5 ? 1 : -1), -250); //normal spawn
level.setPosToSpawn(450 * (Math.random() < 0.5 ? 1 : -1), -300); //normal spawn
// spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //entrance bump disabled for performance
level.exit.x = 0;
level.exit.y = -9000;
// spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 100); //exit bump disabled for performance
const stationWidth = 9000
let stationNumber = 0;
let stationCustom = () => { }
@@ -2562,43 +2610,23 @@ const level = {
train[train.length - 1].isMoving = false
train[train.length - 1].stops = { left: -7225, right: -1725 }
const stationList = [] //organize the possible stations into a random order, with one station removed
for (let i = 0, totalNumberOfStations = 5; i < totalNumberOfStations; ++i) stationList.push(i) //!!!! update station number when you add a new station
const stationList = [] //use to randomize station order
for (let i = 1, totalNumberOfStations = 8; i < totalNumberOfStations; ++i) stationList.push(i) //!!!! update station number when you add a new station
shuffle(stationList);
stationList.splice(Math.floor(Math.random() * stationList.length), 1); //remove one random element from array
stationList.splice(0, 3); //remove some stations to keep it to 4 stations
stationList.unshift(0) //add index zero to the front of the array
let isExitOpen = false
let gatesOpenRight = -1
let gatesOpenLeft = -1
const infrastructure = (x, isInProgress = true) => {
if (isInProgress) {
spawn.setSpawnList(); //picks a couple mobs types for a themed random mob spawns
const mobsLeft = Math.floor(mobSpawnCap - mobs.mobDeaths - 50 / simulation.difficultyMode)
if (mobsLeft > 0) {
simulation.makeTextLog(`<span class='color-var'>${mobsLeft}</span> mobs left before exit opens`);
} else {
isExitOpen = true
simulation.makeTextLog(`exit opened`);
}
function removeAll(array) {
for (let i = 0; i < array.length; ++i) Matter.Composite.remove(engine.world, array[i]);
}
removeAll(map);
map = [];
// removeAll(body); //can't remove bodies because it removes the train
// body = [];
// removeAll(mob);
// mob = [];
// removeAll(bullet);
// bullet = [];
// removeAll(composite);
// composite = [];
// removeAll(cons); // don't allow constraints that don't come from a mob (like elevators, rotors?)
// cons = [];
// removeAll(consBB); // don't allow constraints that don't come from a mob (like elevators, rotors?)
// consBB = [];
// removeAll(powerUp);
// powerUp = [];
//remove any powerUp that is too far from player
for (let i = 0; i < powerUp.length; ++i) {
if (Vector.magnitudeSquared(Vector.sub(player.position, powerUp[i].position)) > 9000000) { //remove any powerUp farther then 3000 pixels from player
@@ -2617,15 +2645,293 @@ const level = {
mob.splice(i--, 1)
}
}
} else {
for (let i = 0; i < 8; ++i) powerUps.chooseRandomPowerUp(100 * (Math.random() - 0.5), -200 - 100 * Math.random())//only spawn heal or ammo once at the first station
}
const checkGate = (gate, gateButton) => {
if (gate) { //check status of buttons and gates
gate.isClosing = gateButton.isUp
gate.openClose(true);
if (gateButton.isUp) {
gateButton.query();
if (!gateButton.isUp) {
if (stationNumber > 0) {
gatesOpenRight = stationNumber
} else if (stationNumber < 0) {
gatesOpenLeft = stationNumber
} else { //starting station both doors open
gatesOpenLeft = stationNumber
gatesOpenRight = stationNumber
}
if (Math.abs(stationNumber) > 0 && ((Math.abs(stationNumber) + 1) % stationList.length) === 0) {
simulation.makeTextLog(`exit opened`);
isExitOpen = true;
}
}
}
gateButton.draw();
}
}
const stations = [ //update totalNumberOfStations as you add more stations
() => { //slime
() => { //empty starting station
if (isExitOpen) {
level.exit.x = x - 665;
level.exit.y = -920;
level.exit.x = x - 50;
level.exit.y = -260;
} else {
var gateButton = level.button(x - 62, -237, 125, false) //x, y, width = 126, isSpawnBase = true
gateButton.isUp = true
if (stationNumber === 0 && gatesOpenRight === -1 && gatesOpenLeft === -1) {
var gateR = level.doorMap(x + 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
var gateL = level.doorMap(x - 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
for (let i = 0; i < 10; ++i) powerUps.chooseRandomPowerUp(x + 800 * (Math.random() - 0.5), -300 - 100 * Math.random())//only spawn heal or ammo once at the first station
}
}
spawn.mapRect(x + -1400, -750, 3375, 100); //roof
spawn.mapRect(x + -1500, -210, 3000, 400);//station floor
// spawn.mapRect(x + -550, -220, 1125, 100); //floor
// spawn.mapRect(x + -475, -230, 975, 150);//floor
spawn.mapVertex(x + 0, -200, "400 0 -400 0 -300 -80 300 -80"); //hexagon but wide
// spawn.mapRect(x + -1350, -550, 50, 150);
// spawn.mapRect(x + 1300, -550, 50, 150);
stationCustom = () => { };
stationCustomTopLayer = () => {
checkGate(gateR, gateButton)
checkGate(gateL, gateButton)
};
},
() => { //portal maze
const buttonsCoords = [{ x: x + 50, y: -1595 }, { x: x + 637, y: -2195 }, { x: x - 1487, y: -2145 }]
const buttonsCoordsIndex = Math.floor(Math.random() * buttonsCoords.length) //pick a random element from the array
if (isExitOpen) {
level.exit.x = buttonsCoords[buttonsCoordsIndex].x;
level.exit.y = buttonsCoords[buttonsCoordsIndex].y - 25;
} else {
var gateButton = level.button(buttonsCoords[buttonsCoordsIndex].x, buttonsCoords[buttonsCoordsIndex].y, 126, false) //x, y, width = 126, isSpawnBase = true
gateButton.isUp = true
if (stationNumber > gatesOpenRight) {
var gate = level.doorMap(x + 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
} else if (stationNumber < gatesOpenLeft) {
var gate = level.doorMap(x - 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
}
}
spawn.mapRect(x + -1500, -210, 3000, 400);//station floor
spawn.mapRect(x + -1775, -1600, 3400, 1100); //center pillar
spawn.mapRect(x + -4100, -3325, 8000, 700); //roof
spawn.mapRect(x + -4100, -3325, 325, 1500);
spawn.mapRect(x + 3500, -3325, 400, 1500);
spawn.mapRect(x + -225, -575, 450, 425); //lower portal blocks
//upper parts
spawn.mapRect(x + -1425, -2400, 1900, 50);
spawn.mapRect(x + -775, -2750, 575, 1045);
spawn.mapRect(x + 475, -1900, 450, 375);
spawn.mapRect(x + 2225, -2300, 125, 350);
spawn.mapRect(x + 2550, -2350, 700, 50);
spawn.mapRect(x + 1375, -2850, 125, 650);
spawn.mapRect(x + 600, -2200, 200, 195);
spawn.mapRect(x + -3500, -2275, 825, 75);
spawn.mapRect(x + -1550, -2150, 250, 250);
spawn.mapRect(x + -2575, -2450, 275, 345);
if (!isExitOpen) {
if (Math.random() < 0.5) {
spawn.randomMob(x + 2850, -2425, 0);
spawn.randomMob(x + 2275, -2425, 0);
spawn.randomMob(x + 2000, -2150, 0);
spawn.randomMob(x + 1650, -2150, 0);
spawn.randomMob(x + 1000, -2475, 0);
spawn.randomMob(x + 725, -2450, 0);
spawn.randomMob(x + 525, -2175, 0);
spawn.randomMob(x + 200, -1950, 0);
spawn.randomMob(x + -25, -1825, 0);
spawn.randomMob(x + -975, -2000, 0);
spawn.randomMob(x + -1500, -2225, 0);
spawn.randomMob(x + 1850, -2125, 0);
spawn.randomMob(x + 225, -1975, 0);
spawn.randomMob(x + 25, -1950, 0);
spawn.randomMob(x + 25, -1950, 0);
} else {
spawn.randomMob(x + 250, -1850, 0);
spawn.randomMob(x + 225, -1950, 0);
spawn.randomMob(x + 125, -2000, 0);
spawn.randomMob(x + 0, -1800, 0);
spawn.randomMob(x + -1725, -2300, 0);
spawn.randomMob(x + -2025, -2175, 0);
spawn.randomMob(x + -2050, -2250, 0);
spawn.randomMob(x + -2000, -2350, 0);
spawn.randomMob(x + -2950, -2400, 0);
spawn.randomMob(x + -2775, -2400, 0);
spawn.randomMob(x + -2425, -2550, 0);
spawn.randomMob(x + 1950, -2225, 0);
spawn.randomMob(x + -2700, -2100, 0);
spawn.randomMob(x + -1925, -2175, 0);
spawn.randomMob(x + -825, -2050, 0);
}
}
const portal1 = level.portal({ x: x - 250, y: -310 }, Math.PI,
{ x: x + -3750, y: -2100 }, 0)
const portal2 = level.portal({ x: x + 250, y: -310 }, 0,
{ x: x + 3475, y: -2100 }, Math.PI)
const portal3 = level.portal({ x: x - 800, y: -2500 }, Math.PI,
{ x: x - 175, y: -2500 }, 0)
const portal4 = level.portal({ x: x + 1275, y: -1700 }, Math.PI,
{ x: x - 1275, y: -1700 }, 0)
stationCustom = () => {
portal1[2].query()
portal1[3].query()
portal2[2].query()
portal2[3].query()
portal3[2].query()
portal3[3].query()
portal4[2].query()
portal4[3].query()
}
stationCustomTopLayer = () => {
checkGate(gate, gateButton)
portal1[0].draw();
portal1[1].draw();
portal1[2].draw();
portal1[3].draw();
portal2[0].draw();
portal2[1].draw();
portal2[2].draw();
portal2[3].draw();
portal3[0].draw();
portal3[1].draw();
portal3[2].draw();
portal3[3].draw();
portal4[0].draw();
portal4[1].draw();
portal4[2].draw();
portal4[3].draw();
}
},
() => { //opening and closing doors
const buttonsCoords = [{ x: x - 800, y: -2245 }, { x: x + 250, y: -870 }, { x: x + 1075, y: -1720 }, { x: x - 1600, y: -1995 }]
const buttonsCoordsIndex = Math.floor(Math.random() * buttonsCoords.length) //pick a random element from the array
if (isExitOpen) {
level.exit.x = buttonsCoords[buttonsCoordsIndex].x;
level.exit.y = buttonsCoords[buttonsCoordsIndex].y - 25;
} else {
var gateButton = level.button(buttonsCoords[buttonsCoordsIndex].x, buttonsCoords[buttonsCoordsIndex].y, 126, false) //x, y, width = 126, isSpawnBase = true
gateButton.isUp = true
if (stationNumber > gatesOpenRight) {
var gate = level.doorMap(x + 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
} else if (stationNumber < gatesOpenLeft) {
var gate = level.doorMap(x - 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
}
}
if (!isExitOpen) {
if (Math.random() < 0.5) {
spawn.randomMob(x + 1125, -650, 0);
spawn.randomMob(x + 150, -950, 0);
spawn.randomMob(x + 100, -975, 0);
spawn.randomMob(x + 75, -975, 0);
spawn.randomMob(x + 275, -1225, 0);
spawn.randomMob(x + 825, -975, 0);
spawn.randomMob(x + -50, -1625, 0);
spawn.randomMob(x + -950, -1550, 0);
spawn.randomMob(x + -975, -1550, 0);
spawn.randomMob(x + -900, -2500, 0);
spawn.randomMob(x + -975, -2550, 0);
spawn.randomMob(x + 675, -1950, 0);
spawn.randomMob(x + 675, -2550, 0);
spawn.randomMob(x + 1225, -1825, 0);
spawn.randomMob(x + -750, -2450, 0);
spawn.randomMob(x + -700, -825, 0);
} else {
spawn.randomMob(x + -675, -675, 0);
spawn.randomMob(x + -575, -925, 0);
spawn.randomMob(x + -425, -1100, 0);
spawn.randomMob(x + -225, -1225, 0);
spawn.randomMob(x + -650, -1250, 0);
spawn.randomMob(x + -675, -775, 0);
spawn.randomMob(x + 75, -1000, 0);
spawn.randomMob(x + -1100, -1575, 0);
spawn.randomMob(x + -1250, -1850, 0);
spawn.randomMob(x + -1625, -2100, 0);
spawn.randomMob(x + -700, -2500, 0);
spawn.randomMob(x + -375, -2550, 0);
spawn.randomMob(x + 250, -2025, 0);
spawn.randomMob(x + 675, -2175, 0);
spawn.randomMob(x + -1000, -2000, 0);
spawn.randomMob(x + -1550, -2325, 0);
spawn.randomMob(x + -1725, -2425, 0);
}
}
spawn.mapRect(x + -1500, -210, 3000, 400);//station floor
spawn.mapRect(x + -2550, -3200, 425, 1375);//roof left wall
spawn.mapRect(x + 2125, -3175, 450, 1375);//roof right wall
spawn.mapRect(x + -2550, -3200, 5125, 225);//roof
spawn.mapRect(x + -1325, -550, 1375, 50);//first floor roof/ground
spawn.mapRect(x + 775, -550, 675, 50);
spawn.mapRect(x + -200, -875, 1300, 50); //2nd floor roof/ground
spawn.mapRect(x + -125, -1125, 50, 275);
spawn.mapRect(x + -125, -1150, 800, 50); //3rd floor roof/ground
spawn.mapRect(x + -1450, -1475, 1600, 50);
spawn.mapRect(x + -1325, -1725, 800, 50); //4th floor roof/ground
spawn.mapRect(x + 50, -1725, 1350, 50);
spawn.mapRect(x + -1125, -2250, 700, 50);
spawn.mapRect(x, -525, 50, 150); //door cap for ground at ground y = -210
const door1 = level.doorMap(x + 12, -380, 25, 170, 140, 20, false) //x, y, width, height, distance, speed = 20
spawn.mapRect(x - 200, -525 - 340, 50, 150); //door cap for ground at ground y = -210
const door2 = level.doorMap(x - 188, -380 - 340, 25, 170, 140, 20, false) //x, y, width, height, distance, speed = 20
spawn.mapRect(x + 100, -525 - 940, 50, 150); //door cap for ground at ground y = -210
const door3 = level.doorMap(x + 112, -380 - 940, 25, 170, 140, 20, false) //x, y, width, height, distance, speed = 20
spawn.mapRect(x + 450, -3050, 50, 775);
const door4 = level.doorMap(x + 462, -2300, 25, 575, 520, 30, false) //x, y, width, height, distance, speed = 20
const portal1 = level.portal({
x: x + 2100,
y: -2100
}, Math.PI, { //right
x: x + -1275,
y: -650
}, 2 * Math.PI) //right
stationCustom = () => {
door1.isClosing = (simulation.cycle % 240) < 120
door1.openClose(true);
door2.isClosing = (simulation.cycle % 240) > 120
door2.openClose(true);
door3.isClosing = (simulation.cycle % 240) < 120
door3.openClose(true);
door4.isClosing = (simulation.cycle % 240) > 120
door4.openClose(true);
portal1[2].query()
portal1[3].query()
}
stationCustomTopLayer = () => {
checkGate(gate, gateButton)
portal1[0].draw();
portal1[1].draw();
portal1[2].draw();
portal1[3].draw();
}
},
() => { //slime
const buttonsCoords = [{ x: x - 675, y: -895 }, { x: x - 750, y: -70 }, { x: x + 75, y: -570 },]
const buttonsCoordsIndex = Math.floor(Math.random() * buttonsCoords.length) //pick a random element from the array
if (isExitOpen) {
level.exit.x = buttonsCoords[buttonsCoordsIndex].x;
level.exit.y = buttonsCoords[buttonsCoordsIndex].y - 25;
} else {
var gateButton = level.button(buttonsCoords[buttonsCoordsIndex].x, buttonsCoords[buttonsCoordsIndex].y, 126, false) //x, y, width = 126, isSpawnBase = true
gateButton.isUp = true
if (stationNumber > gatesOpenRight) {
var gate = level.doorMap(x + 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
} else if (stationNumber < gatesOpenLeft) {
var gate = level.doorMap(x - 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
}
}
spawn.mapRect(x + -1575, -2000, 3025, 100); //roof
// spawn.mapRect(x + -1575, -2200, 3025, 300); //roof
// spawn.mapRect(x + -1500, -210, 3000, 400);//station floor
@@ -2646,7 +2952,7 @@ const level = {
spawn.mapRect(x - 925, -75, 875, 150);
spawn.mapRect(x + 475, -1400, 75, 1250);
if (mobs.mobDeaths < mobSpawnCap) {
if (!isExitOpen) {
if (Math.random() < 0.5) {
spawn.randomMob(x + -850, -450, 0);
spawn.randomMob(x + -850, -125, 0);
@@ -2694,6 +3000,8 @@ const level = {
// drip3.draw();
}
stationCustomTopLayer = () => {
checkGate(gate, gateButton)
hazard1.query();
hazard1.level(isSlimeRiseUp, 1.5)
if (!(hazard1.height < hazard1.maxHeight)) {
@@ -2705,11 +3013,24 @@ const level = {
boost2.query();
}
},
() => { //portals
() => { //portal fling
const buttonsCoords = [{ x: x + 775, y: -1695 }, { x: x - 775, y: -800 }, { x: x - 375, y: -2083 },]
const buttonsCoordsIndex = Math.floor(Math.random() * buttonsCoords.length) //pick a random element from the array
if (isExitOpen) {
level.exit.x = x + 950;
level.exit.y = -1725;
level.exit.x = buttonsCoords[buttonsCoordsIndex].x;
level.exit.y = buttonsCoords[buttonsCoordsIndex].y - 25;
} else {
var gateButton = level.button(buttonsCoords[buttonsCoordsIndex].x, buttonsCoords[buttonsCoordsIndex].y, 126, false) //x, y, width = 126, isSpawnBase = true
gateButton.isUp = true
if (stationNumber > gatesOpenRight) {
var gate = level.doorMap(x + 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
} else if (stationNumber < gatesOpenLeft) {
var gate = level.doorMap(x - 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
}
}
spawn.mapRect(x + -1600, -3450, 300, 1475); //roof
spawn.mapRect(x + -1600, -3450, 3225, 100);
spawn.mapRect(x + 1300, -3450, 325, 1525);
spawn.mapVertex(x + 400, -180, "-300 0 -300 -100 300 -100 400 0");
spawn.mapVertex(x - 400, -180, "300 0 300 -100 -300 -100 -400 0");
@@ -2719,12 +3040,11 @@ const level = {
spawn.mapRect(x + 125, -700, 1225, 200);
spawn.mapRect(x + -1325, -1775, 775, 175);
spawn.mapVertex(x + 445, -800, "-200 0 -200 -300 100 -300 185 0");
spawn.mapVertex(x - 350, -1850, "-185 0 -100 -400 400 -400 400 0");
spawn.mapVertex(x - 310, -1880, "-185 0 -100 -400 400 -400 400 0");
spawn.mapVertex(x + -675, -725, "325 0 250 80 -250 80 -325 0 -250 -80 250 -80");
spawn.mapRect(x + 650, -1700, 725, 550);
spawn.mapRect(x + 1500, -2375, 275, 450);
spawn.mapRect(x + 625, -1700, 750, 500);
if (mobs.mobDeaths < mobSpawnCap) {
if (!isExitOpen) {
spawn.randomMob(x + -750, -1925, 0);
spawn.randomMob(x + -425, -2300, 0);
spawn.randomMob(x + -350, -2200, 0);
@@ -2745,10 +3065,10 @@ const level = {
const portal1 = level.portal({
x: x + 0,
y: -200
}, -Math.PI / 2, { //right
}, -Math.PI / 2, { //up
x: x + 200,
y: -900
}, -Math.PI / 2) //right
}, -Math.PI / 2) //up
const portal2 = level.portal({
x: x + 1275,
y: -800
@@ -2764,6 +3084,7 @@ const level = {
portal2[3].query()
}
stationCustomTopLayer = () => {
checkGate(gate, gateButton)
portal1[0].draw();
portal1[1].draw();
portal1[2].draw();
@@ -2775,10 +3096,21 @@ const level = {
}
},
() => { //tower levels and squares
const buttonsCoords = [{ x: x - 300, y: -3120 }, { x: x + 600, y: -3020 }, { x: x - 575, y: -1770 }, { x: x - 450, y: -2370 }]
const buttonsCoordsIndex = Math.floor(Math.random() * buttonsCoords.length) //pick a random element from the array
if (isExitOpen) {
level.exit.x = x - 450;
level.exit.y = -3150;
level.exit.x = buttonsCoords[buttonsCoordsIndex].x;
level.exit.y = buttonsCoords[buttonsCoordsIndex].y - 25;
} else {
var gateButton = level.button(buttonsCoords[buttonsCoordsIndex].x, buttonsCoords[buttonsCoordsIndex].y, 126, false) //x, y, width = 126, isSpawnBase = true
gateButton.isUp = true
if (stationNumber > gatesOpenRight) {
var gate = level.doorMap(x + 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
} else if (stationNumber < gatesOpenLeft) {
var gate = level.doorMap(x - 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
}
}
spawn.mapRect(x + -1500, -210, 3000, 400);//station floor
spawn.mapRect(x + -1625, -3950, 3225, 350);//roof
spawn.mapRect(x + 1300, -3850, 300, 2150); //roof wall
@@ -2869,20 +3201,28 @@ const level = {
},
]
if (mobs.mobDeaths < mobSpawnCap) mobPlacement[Math.floor(Math.random() * mobPlacement.length)]()//different random mob placements, with mobs clustered to surprise player
if (!isExitOpen) mobPlacement[Math.floor(Math.random() * mobPlacement.length)]()//different random mob placements, with mobs clustered to surprise player
stationCustom = () => { }
stationCustomTopLayer = () => { }
stationCustomTopLayer = () => {
checkGate(gate, gateButton)
}
},
() => { //jump pads and 6 sided platforms
const buttonsCoords = [{ x: x + 275, y: -1817 }, { x: x + 2025, y: -1995 }, { x: x - 2025, y: -2420 }, { x: x - 2100, y: -1995 }]
const buttonsCoordsIndex = Math.floor(Math.random() * buttonsCoords.length) //pick a random element from the array
if (isExitOpen) {
if (Math.random() < 0.5) {
level.exit.x = x - 2075;
level.exit.y = -2450;
level.exit.x = buttonsCoords[buttonsCoordsIndex].x;
level.exit.y = buttonsCoords[buttonsCoordsIndex].y - 25;
} else {
level.exit.x = x + 2250;
level.exit.y = -2020;
var gateButton = level.button(buttonsCoords[buttonsCoordsIndex].x, buttonsCoords[buttonsCoordsIndex].y, 126, false) //x, y, width = 126, isSpawnBase = true
gateButton.isUp = true
if (stationNumber > gatesOpenRight) {
var gate = level.doorMap(x + 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
} else if (stationNumber < gatesOpenLeft) {
var gate = level.doorMap(x - 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
}
}
spawn.mapRect(x + -1500, -210, 3000, 400);//station floor
spawn.mapRect(x + -3200, -3200, 300, 1400); //roof left wall
spawn.mapRect(x + 2600, -3200, 300, 1400);//roof right wall
@@ -2952,21 +3292,32 @@ const level = {
spawn.randomMob(x + -600, -375, 0);
},
]
if (mobs.mobDeaths < mobSpawnCap) mobPlacement[Math.floor(Math.random() * mobPlacement.length)]()//different random mob placements, with mobs clustered to surprise player
if (!isExitOpen) mobPlacement[Math.floor(Math.random() * mobPlacement.length)]()//different random mob placements, with mobs clustered to surprise player
const boost1 = level.boost(x - 50, -225, 790)
const boost2 = level.boost(x + 550, -985, 900)
const boost3 = level.boost(x + -850, -835, 1900)
stationCustom = () => { }
stationCustomTopLayer = () => {
checkGate(gate, gateButton)
boost1.query();
boost2.query();
boost3.query();
}
},
() => { //crouch tunnels
const buttonsCoords = [{ x: x + 625, y: -1395 }, { x: x - 15, y: -1595 }, { x: x - 800, y: -1295 }]
const buttonsCoordsIndex = Math.floor(Math.random() * buttonsCoords.length) //pick a random element from the array
if (isExitOpen) {
level.exit.x = x + 0;
level.exit.y = -1620;
level.exit.x = buttonsCoords[buttonsCoordsIndex].x;
level.exit.y = buttonsCoords[buttonsCoordsIndex].y - 25;
} else {
var gateButton = level.button(buttonsCoords[buttonsCoordsIndex].x, buttonsCoords[buttonsCoordsIndex].y, 126, false) //x, y, width = 126, isSpawnBase = true
gateButton.isUp = true
if (stationNumber > gatesOpenRight) {
var gate = level.doorMap(x + 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
} else if (stationNumber < gatesOpenLeft) {
var gate = level.doorMap(x - 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
}
}
spawn.mapRect(x + -1500, -210, 3000, 400);//station floor
spawn.mapRect(x + -1575, -2200, 3025, 300); //roof
@@ -2981,13 +3332,12 @@ const level = {
spawn.mapRect(x + -225, -525, 800, 210);
spawn.mapRect(x + -100, -1600, 300, 193);
spawn.mapRect(x + 925, -1250, 75, 75);
spawn.bodyRect(x + 625, -1550, 150, 150, 0.3);
spawn.bodyRect(x + 200, -1475, 75, 175, 0.3);
spawn.bodyRect(x + -25, -625, 225, 100, 0.3);
spawn.bodyRect(x + -1000, -750, 125, 175, 0.3);
spawn.bodyRect(x + -625, -1450, 75, 150, 0.3);
spawn.bodyRect(x + -650, -300, 300, 75, 0.3);
if (mobs.mobDeaths < mobSpawnCap) {
if (!isExitOpen) {
spawn.randomMob(x + -750, -1425, 0);
spawn.randomMob(x + -1050, -1100, 0);
spawn.randomMob(x + -825, -750, 0);
@@ -3006,6 +3356,7 @@ const level = {
}
stationCustom = () => { }
stationCustomTopLayer = () => {
checkGate(gate, gateButton)
ctx.fillStyle = "rgba(0,0,0,0.08)"
ctx.fillRect(x + -225, -325, 800, 125);
ctx.fillRect(x + -100, -1425, 300, 125);
@@ -3013,8 +3364,8 @@ const level = {
}
},
]
// stations[0]() //for testing a specific station
stations[stationList[Math.abs((stationNumber + Math.floor(stationList.length / 2)) % stationList.length)]]() //*************** run this one when uploading
// stations[1]() //for testing a specific station
stations[stationList[Math.abs(stationNumber % stationList.length)]]() //*************** run this one when uploading
//add in standard station map infrastructure
spawn.mapRect(x + -8000, 0, 16000, 800);//tunnel floor
spawn.mapRect(x + 1500 - 200, -2000, 6400, 1500); //tunnel roof
@@ -3073,9 +3424,11 @@ const level = {
//track what station the player is in
if (m.pos.x > 0.55 * stationWidth + stationNumber * stationWidth) {
stationNumber++
// if ((stationNumber % stationList.length) == 0) stationNumber++ //skip the stationNumber that is the modulus of the length of the stationList
infrastructure(stationNumber * stationWidth)
} else if (m.pos.x < -0.55 * stationWidth + stationNumber * stationWidth) {
stationNumber--
// if ((stationNumber % stationList.length) == 0) stationNumber--//skip the stationNumber that is the modulus of the length of the stationList
infrastructure(stationNumber * stationWidth)
}
stationCustom()
@@ -17029,7 +17382,6 @@ const level = {
category: cat.body,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
isNoSetCollision: true,
isNotHoldable: true,
frictionAir: 0,
friction: 1,
@@ -17058,7 +17410,6 @@ const level = {
category: cat.body,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
isNoSetCollision: true,
isNotHoldable: true,
frictionAir: 0.01,
friction: 1,
@@ -17120,7 +17471,6 @@ const level = {
category: cat.body,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
isNoSetCollision: true,
isNotHoldable: true,
friction: 1,
frictionStatic: 1,
@@ -17183,7 +17533,6 @@ const level = {
category: cat.body,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
isNoSetCollision: true,
isNotHoldable: true,
frictionAir: frictionAir,
friction: 1,
@@ -17196,7 +17545,6 @@ const level = {
category: cat.body,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
isNoSetCollision: true,
isNotHoldable: true,
frictionAir: 0.01,
friction: 1,
@@ -17321,7 +17669,6 @@ const level = {
category: cat.map,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
isNoSetCollision: true,
inertia: Infinity, //prevents rotation
isNotHoldable: true,
friction: 1,
@@ -17367,7 +17714,6 @@ const level = {
category: cat.map,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
isNoSetCollision: true,
inertia: Infinity, //prevents rotation
isNotHoldable: true,
friction: 1,
@@ -17943,7 +18289,7 @@ const level = {
}
var circleHead = Matter.Bodies.polygon(m.pos.x, m.pos.y, 0, 31);
var losDomain = generateIntersectMap().concat(mob.filter((obj) => { return obj.isNotCloaked == null && (obj.isBoss || obj.label != 'Circle Body') }), [pendulum1, gear1, gear2, player, circleHead]);
var losDomain = generateIntersectMap().concat(mob.filter((obj) => { return obj.isNotCloaked == null && (obj.isBoss || obj.label != 'Circle Body') }), [pendulum1, gear1, gear2, piston1, player, circleHead]);
var oldMap = [...map];
var oldMob = [...mob];
var spawnGearMobCycle = 0;
@@ -17959,7 +18305,7 @@ const level = {
level.custom = () => {
Matter.Body.setPosition(circleHead, m.pos)
if (!(compareArrays(oldMap, map) && compareArrays(oldMob, mob))) {
losDomain = generateIntersectMap().concat(mob.filter((obj) => { return obj.isNotCloaked == null && (obj.isBoss || obj.label != 'Circle Body') }), [pendulum1, gear1, gear2, player, circleHead]);
losDomain = generateIntersectMap().concat(mob.filter((obj) => { return obj.isNotCloaked == null && (obj.isBoss || obj.label != 'Circle Body') }), [pendulum1, gear1, gear2, piston1, player, circleHead]);
}
oldMap = [...map];
oldMob = [...mob];
@@ -17976,8 +18322,7 @@ const level = {
// light
var lightPos = { x: 400, y: -2775 };
var lightRadius = 2950;
const vertices = circleLoS(lightPos, lightRadius, map.concat(mob.filter((obj) => { return obj.isNotCloaked == null && (obj.isBoss || obj.label != 'Circle Body') }), [pendulum1, gear1, gear2, player, circleHead]));
if (vertices.length > 0 && vertices[0].x) {
const vertices = circleLoS(lightPos, lightRadius, map.concat(mob.filter((obj) => { return obj.isNotCloaked == null && (obj.isBoss || obj.label != 'Circle Body') }), [pendulum1, gear1, gear2, piston1, player, circleHead])); if (vertices.length > 0 && vertices[0].x) {
ctx.beginPath();
ctx.moveTo(vertices[0].x, vertices[0].y);
for (var i = 1; i < vertices.length; i++) {

View File

@@ -6103,7 +6103,7 @@ const tech = {
},
{
name: "siphonaptera",
description: "<strong class='color-p' style='letter-spacing: 2px;'>sporangium</strong> and <strong>shotgun</strong> hatch <strong class='color-p' style='letter-spacing: -0.8px;'>fleas</strong>",
description: "<strong class='color-p' style='letter-spacing: 2px;'>spores</strong> metamorphose into <strong class='color-p' style='letter-spacing: -0.8px;'>fleas</strong>",
isGunTech: true,
maxCount: 1,
count: 0,
@@ -6123,7 +6123,7 @@ const tech = {
},
{
name: "nematodes",
description: "<strong>shotgun</strong> and <strong class='color-p' style='letter-spacing: 2px;'>sporangium</strong> hatch <strong class='color-p' style='letter-spacing: -0.8px;'>worms</strong>",
description: "<strong class='color-p' style='letter-spacing: 2px;'>spores</strong> metamorphose into <strong class='color-p' style='letter-spacing: -0.8px;'>worms</strong>",
isGunTech: true,
maxCount: 1,
count: 0,

View File

@@ -1,14 +1,26 @@
******************************************************** NEXT PATCH **************************************************
new community level cantilever
subway
start in an empty station
station exits are blocked by a gate that opens when you press a button
level exit spawns after you clear 4 stations
added 2 new stations (7 possible stations)
extended tech sorting to experiment
laser-bots lasers wiggle as it aims at the target
this is a nerf to iridescence and it looks cool
acetone peroxide does 40->33% more explosion harm to player
fleas can do up to 30% more damage before they despawn
bug fixes
*********************************************************** TODO *****************************************************
only calculate setPaths on subway level?
does this mess with flatland, no
improve flatland performance?
get rid of the word permanent in bot tech
ersatz bots, perimeter defense, network effect
maybe reduce the fps on the line of sight graphics to make it look more like a sensor?
make a bot the follows the player the pov for line of sight levels, not the player
also need to make the vision a slice of a circle, not a full circle
@@ -1228,6 +1240,7 @@ possible names for tech
this is violated by expansion of the universe
https://en.wikipedia.org/wiki/Cosmic_censorship_hypothesis - black holes can't leak
Alcubierre warp drive (FTL with negative mass)
Spherules - A spherule is a small sphere or spherical body. It can also refer to a thick-walled spherical structure that contains endospores and occurs in the parasitic form of fungi
******************************************************** CARS IMAGES ********************************************************