Closed Whyisthisnotavalable closed 1 year ago
looks great
couple things: I had lag when I used my field. I'm guessing that's because the large number of blocks forming the chains. The evangelion video didn't cause lag and was pretty funny. I had a really hard time climbing at the start, maybe the series of small ledges could be a bit wider. I think you go over the limit of power ups. It's capped at 3 max, but I got 4 from 2 bosses.
I just noticed the video continues to play after the level ends.
Yeah I dunno how to fix that
The video is fine, right
the video is fine as long as it ends at the end of the level
if(!level.levels[level.onLevel] == "crimsonTowers") simulation.removeEphemera(this.name);
this doesn't work
if (level.levels[level.onLevel] !== "crimsonTowers") simulation.removeEphemera(this.name);
fixed it
crimsonTowers() {
simulation.makeTextLog(`crimsonTowers by Richard0820. Thank you desboot for this video: <a href="https://www.youtube.com/watch?v=hkdY0mDF2SY&feature=youtu.be&ab_channel=DesBoot">Source</a>`)
const ace = {
spawnOrbitals(who, radius, chance = Math.min(0.25 + simulation.difficulty * 0.005)) {
if (Math.random() < chance) {
// simulation.difficulty = 50
const len = Math.floor(Math.min(15, 3 + Math.sqrt(simulation.difficulty))) // simulation.difficulty = 40 on hard mode level 10
const speed = (0.003 + 0.004 * Math.random() + 0.002 * Math.sqrt(simulation.difficulty)) * ((Math.random() < 0.5) ? 1 : -1)
const offSet = 6.28 * Math.random()
for (let i = 0; i < len; i++) ace.orbital(who, radius, i / len * 2 * Math.PI + offSet, speed)
}
},
orbital(who, radius, phase, speed) {
// for (let i = 0, len = 7; i < len; i++) spawn.orbital(me, radius + 250, 2 * Math.PI / len * i)
mobs.spawn(who.position.x, who.position.y, 8, 12, "rgb(0,0,0)");
let me = mob[mob.length - 1];
me.stroke = "transparent";
Matter.Body.setDensity(me, 0.01); //normal is 0.001
me.leaveBody = false;
me.isDropPowerUp = false;
me.isBadTarget = true;
me.isUnstable = true; //dies when blocked
me.showHealthBar = false;
me.isOrbital = true;
// me.isShielded = true
me.collisionFilter.category = cat.mobBullet;
me.collisionFilter.mask = cat.bullet; //cat.player | cat.map | cat.body
me.do = function () {
//if host is gone
if (!who || !who.alive) {
this.death();
return
}
//set orbit
const time = simulation.cycle * speed + phase
const orbit = {
x: Math.cos(time),
y: Math.sin(time)
}
Matter.Body.setPosition(this, Vector.add(Vector.add(who.position, who.velocity), Vector.mult(orbit, radius)))
//damage player
if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles
const dmg = 0.03 * simulation.dmgScale
m.damage(dmg);
simulation.drawList.push({ //add dmg to draw queue
x: this.position.x,
y: this.position.y,
radius: Math.sqrt(dmg) * 200,
color: simulation.mobDmgColor,
time: simulation.drawTime
});
this.death();
}
};
},
shield(target, x, y, chance = Math.min(0.02 + simulation.difficulty * 0.005, 0.2) + tech.duplicationChance(), isExtraShield = false) {
if (this.allowShields && Math.random() < chance) {
mobs.spawn(x, y, 9, target.radius + 30, "rgba(255,255,255,0.9)");
let me = mob[mob.length - 1];
me.stroke = "rgb(0,0,0)";
Matter.Body.setDensity(me, 0.00001) //very low density to not mess with the original mob's motion
me.shield = true;
me.damageReduction = 0.05 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1)
me.isUnblockable = true
me.isExtraShield = isExtraShield //this prevents spamming with tech.isShieldAmmo
me.collisionFilter.category = cat.mobShield
me.collisionFilter.mask = cat.bullet;
consBB[consBB.length] = Constraint.create({
bodyA: me,
bodyB: target, //attach shield to target
stiffness: 0.4,
damping: 0.1
});
Composite.add(engine.world, consBB[consBB.length - 1]);
me.onDamage = function () {
//make sure the mob that owns the shield can tell when damage is done
this.alertNearByMobs();
this.fill = `rgba(255,255,255,${0.3 + 0.6 * this.health})`
};
me.leaveBody = false;
me.isDropPowerUp = false;
me.showHealthBar = false;
me.shieldTargetID = target.id
target.isShielded = true;
target.shieldID = me.id
me.onDeath = function () {
//clear isShielded status from target
for (let i = 0, len = mob.length; i < len; i++) {
if (mob[i].id === this.shieldTargetID) mob[i].isShielded = false;
}
};
me.do = function () {
this.checkStatus();
};
mob.unshift(me); //move shield to the front of the array, so that mob is behind shield graphically
//swap order of shield and mob, so that mob is behind shield graphically
// mob[mob.length - 1] = mob[mob.length - 2];
// mob[mob.length - 2] = me;
}
},
groupShield(targets, x, y, radius, stiffness = 0.4) {
const nodes = targets.length
mobs.spawn(x, y, 9, radius, "rgba(255,255,255,0.9)");
let me = mob[mob.length - 1];
me.stroke = "rgb(0,0,0)";
Matter.Body.setDensity(me, 0.00001) //very low density to not mess with the original mob's motion
me.frictionAir = 0;
me.shield = true;
me.damageReduction = 0.075 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1)
me.collisionFilter.category = cat.mobShield
me.collisionFilter.mask = cat.bullet;
for (let i = 0; i < nodes; ++i) {
mob[mob.length - i - 2].isShielded = true;
//constrain to all mob nodes in group
consBB[consBB.length] = Constraint.create({
bodyA: me,
bodyB: mob[mob.length - i - 2],
stiffness: stiffness,
damping: 0.1
});
Composite.add(engine.world, consBB[consBB.length - 1]);
}
me.onDamage = function () {
this.alertNearByMobs(); //makes sure the mob that owns the shield can tell when damage is done
this.fill = `rgba(255,255,255,${0.3 + 0.6 * this.health})`
};
me.onDeath = function () {
//clear isShielded status from target
for (let j = 0; j < targets.length; j++) {
for (let i = 0, len = mob.length; i < len; i++) {
if (mob[i].id === targets[j]) mob[i].isShielded = false;
}
}
};
me.leaveBody = false;
me.isDropPowerUp = false;
me.showHealthBar = false;
mob[mob.length - 1] = mob[mob.length - 1 - nodes];
mob[mob.length - 1 - nodes] = me;
me.do = function () {
this.checkStatus();
};
},
slasher2(x, y, radius = 33 + Math.ceil(Math.random() * 30)) {
mobs.spawn(x, y, 6, radius, "rgb(0,0,0)");
let me = mob[mob.length - 1];
Matter.Body.rotate(me, 2 * Math.PI * Math.random());
me.accelMag = 0.0009 * simulation.accelScale;
me.torqueMagnitude = 0.000012 * me.inertia //* (Math.random() > 0.5 ? -1 : 1);
me.frictionStatic = 0;
me.friction = 0;
me.frictionAir = 0.035;
me.delay = 140 * simulation.CDScale;
me.cd = 0;
me.swordRadius = 0;
me.swordVertex = 1
me.swordRadiusMax = 275 + 3.5 * simulation.difficulty;
me.swordRadiusGrowRate = me.swordRadiusMax * (0.011 + 0.0002 * simulation.difficulty)
me.isSlashing = false;
me.swordDamage = 0.03 * simulation.dmgScale
me.laserAngle = 3 * Math.PI / 5
const seeDistance2 = 200000
ace.shield(me, x, y);
me.onDamage = function () { };
me.do = function () {
this.checkStatus();
this.seePlayerByHistory(15);
this.attraction();
this.sword() //does various things depending on what stage of the sword swing
};
me.swordWaiting = function () {
if (
this.seePlayer.recall &&
this.cd < simulation.cycle &&
this.distanceToPlayer2() < seeDistance2 &&
Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 &&
Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0
) {
this.laserAngle = -Math.PI / 6
this.sword = this.swordGrow
this.accelMag = 0
}
}
me.sword = me.swordWaiting //base function that changes during different aspects of the sword swing
me.swordGrow = function () {
this.laserSword(this.vertices[0], this.angle + this.laserAngle);
this.laserSword(this.vertices[1], this.angle + this.laserAngle + (Math.PI / 3));
this.laserSword(this.vertices[2], this.angle + this.laserAngle + (Math.PI * 2 / 3));
this.laserSword(this.vertices[3], this.angle + this.laserAngle + Math.PI);
this.laserSword(this.vertices[4], this.angle + this.laserAngle + (Math.PI * 4 / 3));
this.laserSword(this.vertices[5], this.angle + this.laserAngle + (Math.PI * 5 / 3));
this.swordRadius += this.swordRadiusGrowRate
if (this.swordRadius > this.swordRadiusMax || this.isStunned) {
this.sword = this.swordSlash
this.spinCount = 0
}
}
me.swordSlash = function () {
this.laserSword(this.vertices[0], this.angle + this.laserAngle);
this.laserSword(this.vertices[1], this.angle + this.laserAngle + (Math.PI / 3));
this.laserSword(this.vertices[2], this.angle + this.laserAngle + (Math.PI * 2 / 3));
this.laserSword(this.vertices[3], this.angle + this.laserAngle + Math.PI);
this.laserSword(this.vertices[4], this.angle + this.laserAngle + (Math.PI * 4 / 3));
this.laserSword(this.vertices[5], this.angle + this.laserAngle + (Math.PI * 5 / 3));
this.torque += this.torqueMagnitude;
this.spinCount++
if (this.spinCount > 100 || this.isStunned) {
this.sword = this.swordWaiting
this.swordRadius = 0
this.accelMag = 0.001 * simulation.accelScale;
this.cd = simulation.cycle + this.delay;
}
}
me.laserSword = function (where, angle) {
const vertexCollision = function (v1, v1End, domain) {
for (let i = 0; i < domain.length; ++i) {
let v = domain[i].vertices;
const len = v.length - 1;
for (let j = 0; j < len; j++) {
results = simulation.checkLineIntersection(v1, v1End, v[j], v[j + 1]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[j], v2: v[j + 1] };
}
}
results = simulation.checkLineIntersection(v1, v1End, v[0], v[len]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[0], v2: v[len] };
}
}
};
best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null };
const look = { x: where.x + this.swordRadius * Math.cos(angle), y: where.y + this.swordRadius * Math.sin(angle) };
vertexCollision(where, look, body); // vertexCollision(where, look, mob);
vertexCollision(where, look, map);
if (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]);
if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + m.collisionImmuneCycles + 60; //player is immune to damage for an extra second
m.damage(this.swordDamage);
simulation.drawList.push({ //add dmg to draw queue
x: best.x,
y: best.y,
radius: this.swordDamage * 1500,
color: "rgba(80,0,255,0.5)",
time: 20
});
}
if (best.dist2 === Infinity) best = look;
ctx.beginPath(); //draw beam
ctx.moveTo(where.x, where.y);
ctx.lineTo(best.x, best.y);
ctx.strokeStyle = "rgba(0,0,0,0.1)"; // 0 path
ctx.lineWidth = 15;
ctx.stroke();
ctx.strokeStyle = "rgba(0,0,0,0.5)"; // 0 path
ctx.lineWidth = 4;
ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]);
ctx.stroke(); // Draw it
ctx.setLineDash([]);
}
},
slasher3(x, y, radius = 33 + Math.ceil(Math.random() * 30)) {
const sides = 6
mobs.spawn(x, y, sides, radius, "rgb(0,0,0)");
let me = mob[mob.length - 1];
Matter.Body.rotate(me, 2 * Math.PI * Math.random());
me.accelMag = 0.0005 * simulation.accelScale;
me.frictionStatic = 0;
me.friction = 0;
me.frictionAir = 0.02;
me.delay = 150 * simulation.CDScale;
me.cd = 0;
me.cycle = 0;
me.swordVertex = 1
me.swordRadiusInitial = radius / 2;
me.swordRadius = me.swordRadiusInitial;
me.swordRadiusMax = 750 + 6 * simulation.difficulty;
me.swordRadiusGrowRateInitial = 1.08
me.swordRadiusGrowRate = me.swordRadiusGrowRateInitial//me.swordRadiusMax * (0.009 + 0.0002 * simulation.difficulty)
me.isSlashing = false;
me.swordDamage = 0.04 * simulation.dmgScale
me.laserAngle = 3 * Math.PI / 5
const seeDistance2 = me.swordRadiusMax * me.swordRadiusMax
ace.shield(me, x, y);
me.onDamage = function () { };
me.do = function () {
this.checkStatus();
this.seePlayerByHistory(15);
this.sword() //does various things depending on what stage of the sword swing
};
me.swordWaiting = function () {
this.attraction();
if (
this.seePlayer.recall &&
this.cd < simulation.cycle &&
this.distanceToPlayer2() < seeDistance2 &&
Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 &&
Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0
) {
//find vertex closest to the player
let dist = Infinity
for (let i = 0, len = this.vertices.length; i < len; i++) {
const D = Vector.magnitudeSquared(Vector.sub({ x: this.vertices[i].x, y: this.vertices[i].y }, m.pos))
if (D < dist) {
dist = D
this.swordVertex = i
}
}
this.laserAngle = this.swordVertex / sides * 2 * Math.PI + Math.PI / sides
this.sword = this.swordGrow
this.cycle = 0
this.swordRadius = this.swordRadiusInitial
//slow velocity but don't stop
Matter.Body.setVelocity(this, Vector.mult(this.velocity, 0.5))
//set angular velocity to 50%
// Matter.Body.setAngularVelocity(this, this.angularVelocity * 0.5)
//gently rotate towards the player with a torque, use cross product to decided clockwise or counterclockwise
const laserStartVector = Vector.sub(this.position, this.vertices[this.swordVertex])
const playerVector = Vector.sub(this.position, m.pos)
const cross = Matter.Vector.cross(laserStartVector, playerVector)
this.torque = 0.00002 * this.inertia * (cross > 0 ? 1 : -1)
}
}
me.sword = me.swordWaiting //base function that changes during different aspects of the sword swing
me.swordGrow = function () {
const angle = this.angle + this.laserAngle;
const end = {
x: this.vertices[this.swordVertex].x + this.swordRadiusMax * Math.cos(angle),
y: this.vertices[this.swordVertex].y + this.swordRadiusMax * Math.sin(angle)
};
const dx = end.x - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].x;
const dy = end.y - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].y;
const angle1 = Math.atan2(dy, dx) * (180 / Math.PI);
const dx1 = end.x - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].x;
const dy1 = end.y - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].y;
const angle2 = Math.atan2(dy1, dx1) * (180 / Math.PI);
this.laserSpear(this.vertices[this.swordVertex], this.angle + this.laserAngle);
this.laserSpear(this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1], angle1 * (Math.PI / 180))
this.laserSpear(this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1], angle2 * (Math.PI / 180))
Matter.Body.setVelocity(this, Vector.mult(this.velocity, 0.9))
// this.swordRadius += this.swordRadiusGrowRate
this.cycle++
// this.swordRadius = this.swordRadiusMax * Math.sin(this.cycle * 0.03)
this.swordRadius *= this.swordRadiusGrowRate
if (this.swordRadius > this.swordRadiusMax) this.swordRadiusGrowRate = 1 / this.swordRadiusGrowRateInitial
// if (this.swordRadius > this.swordRadiusMax) this.swordRadiusGrowRate = -Math.abs(this.swordRadiusGrowRate)
if (this.swordRadius < this.swordRadiusInitial || this.isStunned) {
// this.swordRadiusGrowRate = Math.abs(this.swordRadiusGrowRate)
this.swordRadiusGrowRate = this.swordRadiusGrowRateInitial
this.sword = this.swordWaiting
this.swordRadius = 0
this.cd = simulation.cycle + this.delay;
}
}
me.laserSpear = function (where, angle) {
const vertexCollision = function (v1, v1End, domain) {
for (let i = 0; i < domain.length; ++i) {
let v = domain[i].vertices;
const len = v.length - 1;
for (let j = 0; j < len; j++) {
results = simulation.checkLineIntersection(v1, v1End, v[j], v[j + 1]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[j], v2: v[j + 1] };
}
}
results = simulation.checkLineIntersection(v1, v1End, v[0], v[len]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[0], v2: v[len] };
}
}
};
best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null };
const look = { x: where.x + this.swordRadius * Math.cos(angle), y: where.y + this.swordRadius * Math.sin(angle) };
vertexCollision(where, look, body); // vertexCollision(where, look, mob);
vertexCollision(where, look, map);
if (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]);
if (best.who && (best.who === playerBody || best.who === playerHead)) {
this.swordRadiusGrowRate = 1 / this.swordRadiusGrowRateInitial //!!!! this retracts the sword if it hits the player
if (m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + m.collisionImmuneCycles + 60; //player is immune to damage for an extra second
m.damage(this.swordDamage);
simulation.drawList.push({ //add dmg to draw queue
x: best.x,
y: best.y,
radius: this.swordDamage * 1500,
color: "rgba(80,0,255,0.5)",
time: 20
});
}
}
if (best.dist2 === Infinity) best = look;
ctx.beginPath(); //draw beam
ctx.moveTo(where.x, where.y);
ctx.lineTo(best.x, best.y);
ctx.strokeStyle = "rgba(0,0,0,0.1)"; // 0 path
ctx.lineWidth = 15;
ctx.stroke();
ctx.strokeStyle = "rgba(0,0,0,0.5)"; // 0 path
ctx.lineWidth = 4;
ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]);
ctx.stroke(); // Draw it
ctx.setLineDash([]);
}
},
stabber(x, y, radius = 25 + Math.ceil(Math.random() * 12), spikeMax = 7) {
if (radius > 80) radius = 65;
mobs.spawn(x, y, 6, radius, "rgb(0,0,0)"); //can't have sides above 6 or collision events don't work (probably because of a convex problem)
let me = mob[mob.length - 1];
me.isVerticesChange = true
me.accelMag = 0.0006 * simulation.accelScale;
// me.g = 0.0002; //required if using this.gravity
me.isInvulnerable = false
me.delay = 360 * simulation.CDScale;
me.spikeVertex = 0;
me.spikeLength = 0;
me.isSpikeGrowing = false;
me.spikeGrowth = 0;
me.isSpikeReset = true;
me.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.player //can't touch other mobs
Matter.Body.rotate(me, Math.PI * 0.1);
ace.shield(me, x, y);
// me.onDamage = function () {};
// me.onHit = function() { //run this function on hitting player
// };
me.onDeath = function () {
if (this.spikeLength > 4) {
this.spikeLength = 4
const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[this.spikeVertex], this.position)), this.radius * this.spikeLength)
this.vertices[this.spikeVertex].x = this.position.x + spike.x
this.vertices[this.spikeVertex].y = this.position.y + spike.y
// this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices))
}
};
me.do = function () {
this.seePlayerByLookingAt();
this.checkStatus();
this.attraction();
if (this.isSpikeReset) {
if (this.seePlayer.recall) {
const dist = Vector.sub(this.seePlayer.position, this.position);
const distMag = Vector.magnitude(dist);
if (distMag < radius * spikeMax) {
//find nearest vertex
let nearestDistance = Infinity
for (let i = 0, len = this.vertices.length; i < len; i++) {
//find distance to player for each vertex
const dist = Vector.sub(this.seePlayer.position, this.vertices[i]);
const distMag = Vector.magnitude(dist);
//save the closest distance
if (distMag < nearestDistance) {
this.spikeVertex = i
nearestDistance = distMag
}
}
this.spikeLength = 1
this.isSpikeGrowing = true;
this.isSpikeReset = false;
Matter.Body.setAngularVelocity(this, 0)
}
me.isInvulnerable = false
}
} else {
if (this.isSpikeGrowing) {
this.spikeLength += Math.pow(this.spikeGrowth += 0.02, 8)
// if (this.spikeLength < 2) {
// this.spikeLength += 0.035
// } else {
// this.spikeLength += 1
// }
if (this.spikeLength > spikeMax) {
this.isSpikeGrowing = false;
this.spikeGrowth = 0
}
} else {
Matter.Body.setAngularVelocity(this, this.angularVelocity * 0.8) //reduce rotation
this.spikeLength -= 0.3
if (this.spikeLength < 1) {
this.spikeLength = 1
this.isSpikeReset = true
this.radius = radius
}
}
const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[this.spikeVertex], this.position)), radius * this.spikeLength)
this.vertices[this.spikeVertex].x = this.position.x + spike.x
this.vertices[this.spikeVertex].y = this.position.y + spike.y
me.isInvulnerable = true
// this.radius = radius * this.spikeLength;
}
if(this.isInvulnerable) {
ctx.beginPath();
let vertices = this.vertices;
ctx.moveTo(vertices[0].x, vertices[0].y);
for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y);
ctx.lineTo(vertices[0].x, vertices[0].y);
ctx.lineWidth = 13 + 5 * Math.random();
ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`;
ctx.stroke();
me.damageReduction = 0;
} else {
me.damageReduction = 1;
}
};
},
slash(x, y, radius = 80) {
let targets = []
const sides = 6;
mobs.spawn(x, y, 6, radius, "#000000");
let me = mob[mob.length - 1];
Matter.Body.rotate(me, 2 * Math.PI * Math.random());
targets.push(me.id) //add to shield protection
const nodeBalance = Math.random()
const nodes2 = Math.min(15, Math.floor(2 + 4 * nodeBalance + 0.75 * Math.sqrt(simulation.difficulty)))
me.isBoss = true;
me.isSlashBoss = true;
me.showHealthBar = false;
me.damageReduction = 0.1 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1)
me.startingDamageReduction = me.damageReduction
me.isInvulnerable = false
me.frictionAir = 0.02
me.seeAtDistance2 = 1000000;
me.accelMag = 0.0004 + 0.00015 * simulation.accelScale;
Matter.Body.setDensity(me, 0.0005); //normal is 0.001
me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map
me.memory = Infinity;
me.seePlayerFreq = 20
me.lockedOn = null;
me.laserRange = 500;
me.torqueMagnitude = 0.00024 * me.inertia * (Math.random() > 0.5 ? -1 : 1);
me.delay = 70 + 70 * simulation.CDScale;
me.cd = 0;
me.swordRadius = 0;
me.swordVertex = 1
me.swordRadiusMax = 1100 + 20 * simulation.difficulty;
me.swordRadiusGrowRate = me.swordRadiusMax * (0.005 + 0.0003 * simulation.difficulty)
me.isSlashing = false;
me.swordDamage = 0.07 * simulation.dmgScale
me.laserAngle = 3 * Math.PI / 5
me.eventHorizon = 550;
const seeDistance2 = 200000
ace.shield(me, x, y);
const rangeInnerVsOuter = Math.random()
let speed = (0.006 + 0.001 * Math.sqrt(simulation.difficulty)) * ((Math.random() < 0.5) ? 1 : -1)
let range = radius + 350 + 200 * rangeInnerVsOuter + nodes2 * 7
for (let i = 0; i < nodes2; i++) ace.orbital(me, range, i / nodes2 * 2 * Math.PI, speed)
const orbitalIndexes = [] //find indexes for all the current nodes2
for (let i = 0; i < nodes2; i++) orbitalIndexes.push(mob.length - 1 - i)
// add orbitals for each orbital
range = Math.max(60, 100 + 100 * Math.random() - nodes2 * 3 - rangeInnerVsOuter * 80)
speed = speed * (1.25 + 2 * Math.random())
const subNodes = Math.max(2, Math.floor(6 - 5 * nodeBalance + 0.5 * Math.sqrt(simulation.difficulty)))
for (let j = 0; j < nodes2; j++) {
for (let i = 0, len = subNodes; i < len; i++) ace.orbital(mob[orbitalIndexes[j]], range, i / len * 2 * Math.PI, speed)
}
for (let i = 0, len = 3 + 0.5 * Math.sqrt(simulation.difficulty); i < len; i++) ace.spawnOrbitals(me, radius + 40 + 10 * i, 1);
const springStiffness = 0.00014;
const springDampening = 0.0005;
me.springTarget = {
x: me.position.x,
y: me.position.y
};
const len = cons.length;
cons[len] = Constraint.create({
pointA: me.springTarget,
bodyB: me,
stiffness: springStiffness,
damping: springDampening
});
Composite.add(engine.world, cons[cons.length - 1]);
cons[len].length = 100 + 1.5 * radius;
me.cons = cons[len];
me.springTarget2 = {
x: me.position.x,
y: me.position.y
};
const len2 = cons.length;
cons[len2] = Constraint.create({
pointA: me.springTarget2,
bodyB: me,
stiffness: springStiffness,
damping: springDampening,
length: 0
});
Composite.add(engine.world, cons[cons.length - 1]);
cons[len2].length = 100 + 1.5 * radius;
me.cons2 = cons[len2];
me.onDamage = function () { };
me.onDeath = function () {
isDestroyed = true;
this.removeCons();
powerUps.spawnBossPowerUp(this.position.x, this.position.y);
};
me.do = function () {
for(let i = 0; i < this.vertices.length; i++) {
this.harmField(this.vertices[i].x, this.vertices[i].y);
}
this.seePlayerByHistory(40);
this.springAttack();
this.checkStatus();
this.sword() //does various things depending on what stage of the sword swing
const eventHorizon = this.eventHorizon * (1 + 0.2 * Math.sin(simulation.cycle * 0.008))
me.laserRange = eventHorizon;
};
me.swordWaiting = function () {
if (
this.seePlayer.recall &&
this.cd < simulation.cycle &&
this.distanceToPlayer2() < seeDistance2 &&
!m.isCloak &&
Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 &&
Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0
) {
//find vertex farthest away from player
let dist = 0
for (let i = 0, len = this.vertices.length; i < len; i++) {
const D = Vector.magnitudeSquared(Vector.sub({ x: this.vertices[i].x, y: this.vertices[i].y }, m.pos))
if (D > dist) {
dist = D
this.swordVertex = i
}
}
this.laserAngle = this.swordVertex / 6 * 2 * Math.PI + 0.6283
this.sword = this.swordGrow
Matter.Body.setAngularVelocity(this, 0)
this.accelMag = 0.0004 + 0.00015 * simulation.accelScale;
this.damageReduction = 0
this.isInvulnerable = true
this.frictionAir = 1
}
}
me.sword = me.swordWaiting //base function that changes during different aspects of the sword swing
me.swordGrow = function () {
const angle = this.angle + this.laserAngle;
const end = {
x: this.vertices[this.swordVertex].x + this.swordRadiusMax * Math.cos(angle),
y: this.vertices[this.swordVertex].y + this.swordRadiusMax * Math.sin(angle)
};
const dx = end.x - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].x;
const dy = end.y - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].y;
const angle1 = Math.atan2(dy, dx) * (180 / Math.PI);
const dx1 = end.x - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].x;
const dy1 = end.y - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].y;
const angle2 = Math.atan2(dy1, dx1) * (180 / Math.PI);
this.laserSword(this.vertices[this.swordVertex], this.angle + this.laserAngle);
this.laserSword(this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1], angle1 * (Math.PI / 180))
this.laserSword(this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1], angle2 * (Math.PI / 180))
this.swordRadius += this.swordRadiusGrowRate
if (this.swordRadius > this.swordRadiusMax) {
this.sword = this.swordSlash
this.spinCount = 0
}
ctx.beginPath();
let vertices = this.vertices;
ctx.moveTo(vertices[0].x, vertices[0].y);
for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y);
ctx.lineTo(vertices[0].x, vertices[0].y);
ctx.lineWidth = 13 + 5 * Math.random();
ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`;
ctx.stroke();
}
me.swordSlash = function () {
const angle = this.angle + this.laserAngle;
const end = {
x: this.vertices[this.swordVertex].x + this.swordRadiusMax * Math.cos(angle),
y: this.vertices[this.swordVertex].y + this.swordRadiusMax * Math.sin(angle)
};
const dx = end.x - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].x;
const dy = end.y - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].y;
const angle1 = Math.atan2(dy, dx) * (180 / Math.PI);
const dx1 = end.x - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].x;
const dy1 = end.y - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].y;
const angle2 = Math.atan2(dy1, dx1) * (180 / Math.PI);
this.laserSword(this.vertices[this.swordVertex], this.angle + this.laserAngle);
this.laserSword(this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1], angle1 * (Math.PI / 180))
this.laserSword(this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1], angle2 * (Math.PI / 180))
this.torque += this.torqueMagnitude;
this.spinCount++
if (this.spinCount > 80) {
this.sword = this.swordWaiting
this.swordRadius = 0
this.accelMag = 0.0004 + 0.00015 * simulation.accelScale;
this.cd = simulation.cycle + this.delay;
this.damageReduction = this.startingDamageReduction
this.isInvulnerable = false
this.frictionAir = 0.01
}
ctx.beginPath();
let vertices = this.vertices;
ctx.moveTo(vertices[0].x, vertices[0].y);
for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y);
ctx.lineTo(vertices[0].x, vertices[0].y);
ctx.lineWidth = 13 + 5 * Math.random();
ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`;
ctx.stroke();
}
me.laserSword = function (where, angle) {
const vertexCollision = function (v1, v1End, domain) {
for (let i = 0; i < domain.length; ++i) {
let v = domain[i].vertices;
const len = v.length - 1;
for (let j = 0; j < len; j++) {
results = simulation.checkLineIntersection(v1, v1End, v[j], v[j + 1]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[j], v2: v[j + 1] };
}
}
results = simulation.checkLineIntersection(v1, v1End, v[0], v[len]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[0], v2: v[len] };
}
}
};
best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null };
const look = { x: where.x + this.swordRadius * Math.cos(angle), y: where.y + this.swordRadius * Math.sin(angle) };
vertexCollision(where, look, body); // vertexCollision(where, look, mob);
vertexCollision(where, look, map);
if (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]);
if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + m.collisionImmuneCycles + 60; //player is immune to damage for an extra second
m.damage(this.swordDamage);
simulation.drawList.push({ //add dmg to draw queue
x: best.x,
y: best.y,
radius: this.swordDamage * 1500,
color: "rgba(0,0,0,0.5)",
time: 20
});
}
if (best.dist2 === Infinity) best = look;
ctx.beginPath(); //draw beam
ctx.moveTo(where.x, where.y);
ctx.lineTo(best.x, best.y);
ctx.strokeStyle = "rgba(0,0,0,0.1)"; // Black path
ctx.lineWidth = 25;
ctx.stroke();
ctx.strokeStyle = "rgba(0,0,0,0.5)"; // Black path
ctx.lineWidth = 5;
ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]);
ctx.stroke(); // Draw it
ctx.setLineDash([]);
}
me.harmField = function (x, y) {
ctx.setLineDash([125 * Math.random(), 125 * Math.random()]);
// ctx.lineDashOffset = 6*(simulation.cycle % 215);
if (this.distanceToPlayer3(x, y) < this.laserRange) {
if (m.immuneCycle < m.cycle) {
m.damage(0.0003 * simulation.dmgScale);
if (m.energy > 0.1) m.energy -= 0.003
}
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(m.pos.x, m.pos.y);
ctx.lineTo(m.pos.x + (Math.random() - 0.5) * 3000, m.pos.y + (Math.random() - 0.5) * 3000);
ctx.lineWidth = 2;
ctx.strokeStyle = "rgb(0,0,0)";
ctx.stroke();
ctx.beginPath();
ctx.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI);
ctx.fillStyle = "rgba(0,0,0,0.15)";
ctx.fill();
}
ctx.beginPath();
ctx.arc(x, y, this.laserRange * 0.9, 0, 2 * Math.PI);
ctx.strokeStyle = "rgba(0,0,0,0.5)";
ctx.lineWidth = 1;
ctx.stroke();
ctx.setLineDash([]);
ctx.fillStyle = "rgba(0,0,0,0.03)";
ctx.fill();
}
me.distanceToPlayer3 = function (x, y) {
const dx = x - player.position.x;
const dy = y - player.position.y;
return Math.sqrt(dx * dx + dy * dy);
}
radius = 22 // radius of each node mob
const sideLength = 100 // distance between each node mob
const nodes = 6
const angle = 2 * Math.PI / nodes
spawn.allowShields = false; //don't want shields on individual mobs
for (let i = 0; i < nodes; ++i) {
ace.stabber(x + sideLength * Math.sin(i * angle), y + sideLength * Math.cos(i * angle), radius, 12);
Matter.Body.setDensity(mob[mob.length - 1], 0.003); //extra dense //normal is 0.001 //makes effective life much larger
mob[mob.length - 1].damageReduction = 0.12
mob[mob.length - 1].showHealthBar = false;
mob[mob.length - 1].isBoss = true;
targets.push(mob[mob.length - 1].id) //track who is in the node boss, for shields
}
const attachmentStiffness = 0.02
spawn.constrain2AdjacentMobs(nodes, attachmentStiffness, true); //loop mobs together
for (let i = 0; i < nodes; ++i) { //attach to center mob
consBB[consBB.length] = Constraint.create({
bodyA: me,
bodyB: mob[mob.length - i - 1],
stiffness: attachmentStiffness,
damping: 0.03
});
Composite.add(engine.world, consBB[consBB.length - 1]);
}
//spawn shield around all nodes
ace.groupShield(targets, x, y, sideLength + 1 * radius + nodes * 5 - 25);
spawn.allowShields = true;
},
}
level.setPosToSpawn(0, -50);
color.map = "crimson";
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
spawn.mapRect(0, 0, 1, 1);
level.defaultZoom = 1800;
simulation.zoomTransition(level.defaultZoom);
document.body.style.backgroundColor = "#d8dadf";
const isSus = Math.random() < 0.001; //A very lucky person gets rickrolled
const mediaSource = isSus ? "https://ia801509.us.archive.org/10/items/Rick_Astley_Never_Gonna_Give_You_Up/Rick_Astley_Never_Gonna_Give_You_Up.ogv" : "https://cdn.glitch.me/b559a783-c0cb-4369-92e3-0c0a5556ba01/n-gon%20evangelion%20-%20Made%20with%20Clipchamp%20(8).mp4?v=1692134040246"
let videoContainer;
let video = document.createElement("video");
video.src = mediaSource;
video.autoPlay = true;
video.loop = true;
video.muted = true;
videoContainer = {
video : video,
ready : true,
};
video.play();
const boost1 = level.boost(8835, -3675, 7500);
const boost2 = level.boost(-8935, -3675, 7500);
ace.slash(0, -15000 + 1800);
function Raindrop(minX, minY, maxX, maxY) {
this.x = minX + Math.random() * (maxX - minX);
this.y = minY + Math.random() * (maxY - minY);
this.speed = Math.random() * 5 + 25;
this.length = Math.random() * 20 + 30;
}
function forceField(x, y, width, height) {
return {
min: { x: x, y: y },
max: { x: x + width, y: y + height },
width: width,
height: height,
maxHeight: height,
raindrops: [],
drawRaindrop(drop) {
if(Math.sqrt(Math.pow(player.position.x - drop.x, 2) + Math.pow(player.position.y - drop.y, 2)) + Math.PI < 5000) {
ctx.beginPath();
ctx.moveTo(drop.x, drop.y);
ctx.lineTo(drop.x, drop.y + drop.length);
ctx.strokeStyle = '#00FFFF';
ctx.lineWidth = 10;
ctx.lineCap = 'butt';
ctx.stroke();
}
},
updateRaindrop(drop) {
drop.y += drop.speed;
if((Matter.Query.ray(map, { x: drop.x, y: drop.y }, { x: drop.x, y: drop.y - drop.length }).length === 0) == false) {
simulation.drawList.push({
x: drop.x,
y: drop.y - drop.length,
radius: 10,
color: "rgb(0,100,250,0.3)",
time: 8
});
do {
drop.y = this.min.y + this.height * Math.random();
drop.x = this.min.x + this.width * Math.random();
} while (drop.x > this.min.x && drop.x < this.max.x && drop.y > this.min.y && drop.y < this.max.y)
}
},
isOn: true,
query() {
if (this.isOn) {
ctx.fillStyle = `rgba(200, 20, 10, 0.55)`
ctx.fillRect(this.min.x, this.min.y, this.width, this.height)
if (this.height > 0 && Matter.Query.region([player], this).length) {
player.force.y -= 0.015;
m.energy = m.maxEnergy;
}
// if(this.raindrops.length < 300) { // too many (like 900) can cause a little bit of lag minus 5 ~ 10 fps, but it really just depends on your computer
// this.raindrops.push(new Raindrop());
// }
// for (let i = 0; i < this.raindrops.length; i++) {
// const drop = this.raindrops[i];
// this.drawRaindrop(drop);
// this.updateRaindrop(drop);
// }
}
},
}
}
const forceField1 = forceField(-750, -30000, 1500, 20000);
level.custom = () => {
if(player.position.y < -20000) {
level.nextLevel();
}
forceField1.query();
boost1.query();
boost2.query();
level.exit.drawAndCheck();
level.enter.draw();
ctx.beginPath();
ctx.strokeStyle = "rgba(220, 20, 10, 0.55)";
ctx.lineWidth = 1500;
ctx.lineJoin = "miter"
ctx.miterLimit = 100;
ctx.moveTo(map[272].vertices[0].x, map[272].vertices[0].y);
for(let i = 0; i < map[272].vertices.length; i++) {
ctx.lineTo(map[272].vertices[i].x, map[272].vertices[i].y);
}
ctx.closePath();
ctx.stroke();
};
let checkVid = () => {
if(simulation.paused && !videoContainer.paused) {
videoContainer.paused = true;
video.pause();
} else if(!simulation.paused && videoContainer.paused) {
videoContainer.paused = false;
video.play();
}
requestAnimationFrame(checkVid);
}
checkVid();
simulation.ephemera.push({
name: "vid",
do() {
if (level.levels[level.onLevel] !== "crimsonTowers") simulation.removeEphemera(this.name);
if(mediaSource && !isSus) {
ctx.drawImage(videoContainer.video, -1600, -15000, 3200, 1800);
} else if(mediaSource) {
ctx.drawImage(videoContainer.video, -1920 / 2, -15000, 1920, 1080);
}
}
});
level.customTopLayer = () => {
ctx.fillStyle = "rgba(220, 20, 10, 0.1)";
ctx.fillRect(-6725, -3500, 475, 2925);
ctx.fillRect(-8725, -3700, 450, 2925);
ctx.fillRect(-4725, -3300, 450, 2925);
ctx.fillRect(-2725, -3100, 450, 2925);
ctx.fillRect(-725, -2900, 450, 2925);
ctx.fillRect(275, -2900, 450, 2925);
ctx.fillRect(2275, -3100, 450, 2925);
ctx.fillRect(4275, -3300, 450, 2925);
ctx.fillRect(6275, -3500, 450, 2925);
ctx.fillRect(8275, -3700, 450, 2925);
};
spawn.mapRect(-10000, 0, 20000, 2000);
spawn.mapRect(-9050, -3650, 350, 50);
spawn.mapRect(8700, -3650, 350, 50);
spawn.mapRect(-275, -2825, 550, 50);
spawn.mapRect(-225, -500, 450, 50);
spawn.mapRect(-250, -1575, 500, 50);
function spawnTower(index, y = 0) {
const x = index - 1325;
spawn.mapRect(x + 1025, y + -950, 125, 750);
spawn.mapRect(x + 1125, y + -225, 50, 50);
spawn.mapRect(x + 1500, y + -950, 125, 750);
spawn.mapRect(x + 1475, y + -225, 50, 50);
spawn.mapRect(x + 1600, y + -225, 50, 50);
spawn.mapRect(x + 1000, y + -225, 50, 50);
spawn.mapRect(x + 1475, y + -475, 50, 50);
spawn.mapRect(x + 1125, y + -750, 50, 50);
spawn.mapRect(x + 1050, y + -2025, 100, 1125);
spawn.mapRect(x + 1500, y + -2025, 100, 1125);
spawn.mapRect(x + 1475, y + -1050, 50, 50);
spawn.mapRect(x + 1125, y + -1325, 50, 50);
spawn.mapRect(x + 1475, y + -1550, 50, 50);
spawn.mapRect(x + 1125, y + -1875, 50, 50);
spawn.mapRect(x + 1075, y + -2900, 75, 925);
spawn.mapRect(x + 1500, y + -2900, 75, 925);
spawn.mapRect(x + 1475, y + -2150, 50, 50);
spawn.mapRect(x + 1125, y + -2475, 50, 50);
spawn.mapRect(x + 1475, y + -2800, 50, 50);
spawn.mapRect(x + 1000, y + -975, 50, 50);
spawn.mapRect(x + 1025, y + -2050, 50, 50);
spawn.mapRect(x + 1050, y + -2925, 50, 50);
spawn.mapRect(x + 1550, y + -2925, 50, 50);
spawn.mapRect(x + 1600, y + -975, 50, 50);
spawn.mapRect(x + 1575, y + -2050, 50, 50);
for(let i = 0; i < 5; i++) {
if(Math.random() > 0.5) {ace.slasher2(index, y - (i * 500) - 500)} else {ace.slasher3(index, y - (i * 500) - 500)};
}
}
// ace.slash(0, -5000);
function spawnChain(x, y, x1, y1, length = 39) {
const angle = Math.atan2(y1 - y, x1 - x);
chain(x, y, angle, true, length);
}
function chain(x, y, angle = 0, isAttached = true, len = 15, radius = 20, stiffness = 1, damping = 1) {
const gap = 2 * radius
const unit = {
x: Math.cos(angle),
y: Math.sin(angle)
}
for (let i = 0; i < len; i++) {
bullet[bullet.length] = Bodies.polygon(x + gap * unit.x * i, y + gap * unit.y * i, 12, radius, {
inertia: Infinity,
isNotHoldable: true
});
const who = bullet[bullet.length - 1];
who.do = () => {};
who.collisionFilter.category = cat.body;
who.collisionFilter.mask = cat.player | cat.bullet | cat.body | cat.bullet | cat.bullet | cat.bulletBullet
Composite.add(engine.world, who); //add to world
who.classType = "bullet"
}
for (let i = 1; i < len; i++) {
consBB[consBB.length] = Constraint.create({
bodyA: bullet[bullet.length - i],
bodyB: bullet[bullet.length - i - 1],
stiffness: stiffness,
damping: damping
});
Composite.add(engine.world, consBB[consBB.length - 1]);
}
cons[cons.length] = Constraint.create({
pointA: {
x: x,
y: y
},
bodyB: bullet[bullet.length - len],
stiffness: 1,
damping: damping
});
Composite.add(engine.world, cons[cons.length - 1]);
if (isAttached) {
cons[cons.length] = Constraint.create({
pointA: {
x: x + gap * unit.x * (len - 1),
y: y + gap * unit.y * (len - 1)
},
bodyB: bullet[bullet.length - 1],
stiffness: 1,
damping: damping
});
Composite.add(engine.world, cons[cons.length - 1]);
}
}
spawnChain(-2250, -3100, -750, -2900);
spawnChain(-4250, -3300, -2750, -3100);
spawnChain(-6250, -3500, -4750, -3300);
spawnChain(-8250, -3700, -6750, -3500);
spawnChain(750, -2900, 2250, -3100);
spawnChain(2750, -3100, 4250, -3300);
spawnChain(4750, -3300, 6250, -3500);
spawnChain(6750, -3500, 8250, -3700);
spawnChain(-3000, -30000, -9500, -20400, 291);
spawnChain(3000, -30000, 9500, -20400, 291);
spawnTower(500);
spawnTower(2500, -200);
spawn.mapRect(2000, -200, 7000, 300);
spawnTower(4500, -400);
spawn.mapRect(4000, -400, 5000, 300);
spawnTower(6500, -600);
spawn.mapRect(6000, -600, 5000, 300);
spawnTower(8500, -800);
spawn.mapRect(8000, -800, 3000, 300);
spawnTower(-500);
spawnTower(-2500, -200);
spawn.mapRect(-10000, -200, 8000, 300);
spawnTower(-4500, -400);
spawn.mapRect(-10000, -400, 6000, 300);
spawnTower(-6500, -600);
spawn.mapRect(-10000, -600, 4000, 300);
spawnTower(-8500, -800);
spawn.mapRect(-10000, -800, 2000, 300);
spawn.mapVertex(10000, -9450, "-1000 0 1000 0 1000 -10000 500 -20000 -500 -20000 -1000 -10000");
spawn.mapVertex(-10000, -9450, "-1000 0 1000 0 1000 -10000 500 -20000 -500 -20000 -1000 -10000");
spawn.mapRect(-11000, -675, 2000, 2675);
spawn.mapRect(9000, -675, 2000, 2675);
spawn.mapVertex(0, -30000, "0 0 3000 -10000 6000 0 3000 10000");
spawn.mapRect(-8750, -10000, 8000, 100);
spawn.mapRect(750, -10000, 8000, 100);
spawn.mapVertex(0, -10020, "-1000 0 -5000 300 5000 300 1000 0");
spawn.mapRect(-800, -10250, 100, 350);
spawn.mapRect(700, -10250, 100, 350);
const a = 200;
const maxTheta = 10 * Math.PI;
const spiralX = (theta) => a * theta * Math.cos(theta);
const spiralY = (theta) => a * theta * Math.sin(theta);
for (let i = 1; i <= maxTheta; i += 0.2) {
const x = spiralX(i);
const y = spiralY(i) + (-15000 + 1800 / 2);
spawn.mapRect(x, y, 100, 100);
}
level.exit.y = map[272].position.y;
level.exit.x = map[272].position.x;
},
Everything should be fixed here.
ok I'll add to next patch
For performance I removed // spawnChain(-3000, -30000, -9500, -20400, 291); // spawnChain(3000, -30000, 9500, -20400, 291); since the player never sees those chains and chains are poorly optimized even as bullets. Hope you don't mind.
Sure.