1//create array of mobs 2let mob = []; 3//method to populate the array above 4const mobs = { 5 loop() { 6 let i = mob.length; 7 while (i--) { 8 if (mob[i].alive) { 9 mob[i].do(); 10 } else { 11 mob[i].replace(i); //removing mob and replace with body, this is done here to avoid an array index bug with drawing I think 12 } 13 } 14 }, 15 draw() { 16 ctx.lineWidth = 2; 17 let i = mob.length; 18 while (i--) { 19 ctx.beginPath(); 20 const vertices = mob[i].vertices; 21 ctx.moveTo(vertices[0].x, vertices[0].y); 22 for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y); 23 ctx.lineTo(vertices[0].x, vertices[0].y); 24 ctx.fillStyle = mob[i].fill; 25 ctx.strokeStyle = mob[i].stroke; 26 ctx.fill(); 27 ctx.stroke(); 28 } 29 }, 30 healthBar() { 31 for (let i = 0, len = mob.length; i < len; i++) { 32 if (mob[i].seePlayer.recall && mob[i].showHealthBar) { 33 const h = mob[i].radius * 0.3; 34 const w = mob[i].radius * 2; 35 const x = mob[i].position.x - w / 2; 36 const y = mob[i].position.y - w * 0.7; 37 ctx.fillStyle = "rgba(100, 100, 100, 0.3)"; 38 ctx.fillRect(x, y, w, h); 39 ctx.fillStyle = "rgba(255,0,0,0.7)"; 40 ctx.fillRect(x, y, w * mob[i].health, h); 41 } 42 } 43 }, 44 statusSlow(who, cycles = 60) { 45 applySlow(who) 46 //look for mobs near the target 47 if (tech.isAoESlow) { 48 const range2 = (200 + 170 * Math.random()) ** 2 49 for (let i = 0, len = mob.length; i < len; i++) { 50 if (who !== mob[i] && Vector.magnitudeSquared(Vector.sub(who.position, mob[i].position)) < range2 + mob[i].radius) applySlow(mob[i]) 51 } 52 simulation.drawList.push({ 53 x: who.position.x, 54 y: who.position.y, 55 radius: Math.sqrt(range2), 56 color: "rgba(0,100,255,0.05)", 57 time: simulation.drawTime 58 }); 59 } 60 61 function applySlow(whom) { 62 if (!whom.shield && !whom.isShielded && whom.alive) { 63 if (tech.isIceMaxHealthLoss && whom.health > 0.65 && whom.damageReduction > 0) whom.health = 0.66 64 if (tech.isIceKill && whom.health < 0.34 && whom.damageReduction > 0 && whom.alive) { 65 // whom.death(); 66 whom.damage(Infinity); 67 simulation.drawList.push({ 68 x: whom.position.x, 69 y: whom.position.y, 70 radius: whom.radius * 1.2, 71 color: "rgb(0,100,255)", 72 time: 8 73 }); 74 simulation.drawList.push({ 75 x: whom.position.x, 76 y: whom.position.y, 77 radius: whom.radius * 0.7, 78 color: "rgb(0,100,255)", 79 time: 12 80 }); 81 simulation.drawList.push({ 82 x: whom.position.x, 83 y: whom.position.y, 84 radius: whom.radius * 0.4, 85 color: "rgb(0,100,255)", 86 time: 16 87 }); 88 } 89 if (whom.isBoss) cycles = Math.floor(cycles * 0.25) 90 let i = whom.status.length 91 while (i--) { 92 if (whom.status[i].type === "slow") whom.status.splice(i, 1); //remove other "slow" effects on this mob 93 } 94 whom.isSlowed = true; 95 whom.status.push({ 96 effect() { 97 if (whom.speed > 1) { 98 const drag = 0.94 99 Matter.Body.setVelocity(whom, { 100 x: whom.velocity.x * drag, 101 y: whom.velocity.y * drag 102 }); 103 } 104 Matter.Body.setAngularVelocity(whom, 0); 105 ctx.beginPath(); 106 ctx.moveTo(whom.vertices[0].x, whom.vertices[0].y); 107 for (let j = 1, len = whom.vertices.length; j < len; ++j) { 108 ctx.lineTo(whom.vertices[j].x, whom.vertices[j].y); 109 } 110 ctx.lineTo(whom.vertices[0].x, whom.vertices[0].y); 111 ctx.strokeStyle = "rgba(0,100,255,0.8)"; 112 ctx.lineWidth = 15; 113 ctx.stroke(); 114 ctx.fillStyle = whom.fill 115 ctx.fill(); 116 }, 117 endEffect() { 118 //check to see if there are not other freeze effects? 119 whom.isSlowed = false;
120 }, 121 type: "slow", 122 endCycle: simulation.cycle + cycles, 123 }) 124 } 125 } 126 }, 127 statusStun(who, cycles = 180) { 128 if (!who.shield && !who.isShielded) { 129 if (who.speed > 3) { 130 Matter.Body.setVelocity(who, { 131 x: who.velocity.x * 0.8, 132 y: who.velocity.y * 0.8 133 }); 134 } 135 Matter.Body.setAngularVelocity(who, who.angularVelocity * 0.8); 136 //remove other "stun" effects on this mob 137 let i = who.status.length 138 while (i--) { 139 if (who.status[i].type === "stun") who.status.splice(i, 1); 140 } 141 who.isStunned = true; 142 who.status.push({ 143 effect() { 144 if (who.memory !== Infinity) { 145 who.seePlayer.yes = false; 146 who.seePlayer.recall = 0; 147 who.seePlayer.position = { 148 x: who.position.x + 100 * (Math.random() - 0.5), 149 y: who.position.y + 100 * (Math.random() - 0.5) 150 } 151 } else { 152 Matter.Body.setVelocity(who, { 153 x: who.velocity.x * 0.6, 154 y: who.velocity.y * 0.6 155 }); 156 } 157 if (who.velocity.y < 2) who.force.y += who.mass * 0.0004 //extra gravity 158 159 //draw health bar 160 const h = who.radius * 0.3; 161 const w = who.radius * 2; 162 const x = who.position.x - w / 2; 163 const y = who.position.y - w * 0.7; 164 ctx.fillStyle = "rgba(100, 100, 100, 0.3)"; 165 ctx.fillRect(x, y, w, h); 166 ctx.fillStyle = `rgba(${Math.floor(255 * Math.random())},${Math.floor(255 * Math.random())},${Math.floor(255 * Math.random())},0.5)` 167 ctx.fillRect(x, y, w * who.health, h); 168 169 //draw fill inside mob 170 ctx.beginPath(); 171 ctx.moveTo(who.vertices[0].x, who.vertices[0].y); 172 for (let j = 1, len = who.vertices.length; j < len; ++j) { 173 ctx.lineTo(who.vertices[j].x, who.vertices[j].y); 174 } 175 ctx.lineTo(who.vertices[0].x, who.vertices[0].y); 176 ctx.fill(); 177 }, 178 endEffect() { 179 who.isStunned = false 180 }, 181 type: "stun", 182 endCycle: simulation.cycle + cycles * (who.isBoss ? 0.2 : 1), 183 }) 184 } 185 }, 186 statusDoT(who, tickDamage, cycles = 180) { 187 if (!who.isShielded && who.alive && who.damageReduction > 0) { 188 who.status.push({ 189 effect() { 190 if ((simulation.cycle - this.startCycle) % 30 === 0) { 191 let dmg = m.dmgScale * this.dmg * tech.radioactiveDamage 192 who.damage(dmg); 193 if (who.damageReduction) { 194 simulation.drawList.push({ //add dmg to draw queue 195 x: who.position.x + (Math.random() - 0.5) * who.radius * 0.5, 196 y: who.position.y + (Math.random() - 0.5) * who.radius * 0.5, 197 radius: Math.log(dmg + 1.1) * 40 * who.damageReduction + 3, 198 color: "rgba(0,80,80,0.9)", 199 time: simulation.drawTime 200 }); 201 } 202 } 203 }, 204 endEffect() { }, 205 dmg: tickDamage, 206 type: "dot", 207 endCycle: simulation.cycle + cycles, 208 startCycle: simulation.cycle + 29 //makes sure it doesn't tick on first application 209 }) 210 } 211 }, 212 // statusBurn(who, tickDamage, cycles = 90 + Math.floor(90 * Math.random())) { 213 // if (!who.isShielded) { 214 // //remove other "burn" effects on this mob 215 // let i = who.status.length 216 // while (i--) { 217 // if (who.status[i].type === "burn") who.status.splice(i, 1); 218 // } 219 // who.status.push({ 220 // effect() { 221 // if ((simulation.cycle - this.startCycle) % 15 === 0) { 222 // let dmg = m.dmgScale * tickDamage * 0.5 * (1 + Math.random()) 223 // who.damage(dmg); 224 // simulation.drawList.push({ //add dmg to draw queue 225 // x: who.position.x, 226 // y: who.position.y, 227 // radius: Math.log(2 * dmg + 1.1) * 40, 228 // color: `rgba(255,${Math.floor(200*Math.random())},0,0.9)`, 229 // time: simulation.drawTime 230 // }); 231 // } 232 // }, 233 // type: "burn", 234 // endCycle: simulation.cycle + cycles, 235 // startCycle: simulation.cycle 236 // }) 237 // } 238 // }, 239 deathCount: 0, 240 mobSpawnWithHealth: 1, 241 setMobSpawnHealth() { 242 mobs.mobSpawnWithHealth = 0.88 ** (tech.mobSpawnWithHealth) //+ (m.fieldMode === 0 || m.fieldMode === 7) * m.coupling 243 }, 244 //********************************************************************************************** 245 //********************************************************************************************** 246 spawn(xPos, yPos, sides, radius, color) { 247 let i = mob.length;
248 mob[i] = Matter.Bodies.polygon(xPos, yPos, sides, radius, { 249 //inertia: Infinity, //prevents rotation 250 mob: true, 251 density: 0.001, 252 //friction: 0, 253 frictionAir: 0.005, 254 //frictionStatic: 0, 255 restitution: 0.5, 256 collisionFilter: { 257 group: 0, 258 category: cat.mob, 259 mask: cat.player | cat.map | cat.body | cat.bullet | cat.mob 260 }, 261 onHit: undefined, 262 alive: true, 263 index: i, 264 health: mobs.mobSpawnWithHealth, 265 showHealthBar: true, 266 accelMag: 0.001 * simulation.accelScale, 267 cd: 0, //game cycle when cooldown will be over 268 delay: 60, //static: time between cooldowns 269 fill: color, 270 stroke: "#000", 271 seePlayer: { 272 yes: false, 273 recall: 0, 274 position: { 275 x: xPos, 276 y: yPos 277 } 278 }, 279 radius: radius, 280 spawnPos: { 281 x: xPos, 282 y: yPos 283 }, 284 status: [], // [ { effect(), endCycle } ] 285 checkStatus() { 286 let j = this.status.length; 287 while (j--) { 288 this.status[j].effect(); 289 if (this.status[j].endCycle < simulation.cycle) { 290 this.status[j].endEffect(); 291 this.status.splice(j, 1); 292 } 293 } 294 }, 295 isSlowed: false, 296 isStunned: false, 297 seeAtDistance2: Infinity, //sqrt(4000000) = 2000 = max seeing range 298 distanceToPlayer() { 299 const dx = this.position.x - player.position.x; 300 const dy = this.position.y - player.position.y; 301 return Math.sqrt(dx * dx + dy * dy); 302 }, 303 distanceToPlayer2() { 304 const dx = this.position.x - player.position.x; 305 const dy = this.position.y - player.position.y; 306 return dx * dx + dy * dy; 307 }, 308 gravity() { 309 this.force.y += this.mass * this.g; 310 }, 311 seePlayerFreq: Math.floor(30 + 30 * Math.random()), //how often NPC checks to see where player is, lower numbers have better vision 312 foundPlayer() { 313 this.locatePlayer(); 314 if (!this.seePlayer.yes) { 315 this.alertNearByMobs(); 316 this.seePlayer.yes = true; 317 } 318 }, 319 lostPlayer() { 320 this.seePlayer.yes = false; 321 this.seePlayer.recall -= this.seePlayerFreq; 322 if (this.seePlayer.recall < 0) this.seePlayer.recall = 0; 323 }, 324 memory: 120, //default time to remember player's location 325 locatePlayer() { // updates mob's memory of player location 326 this.seePlayer.recall = this.memory + Math.round(this.memory * Math.random()); //cycles before mob falls a sleep 327 this.seePlayer.position.x = player.position.x; 328 this.seePlayer.position.y = player.position.y; 329 }, 330 alertNearByMobs() { 331 //this.alertRange2 is set at the very bottom of this mobs, after mob is made 332 for (let i = 0; i < mob.length; i++) { 333 if (!mob[i].seePlayer.recall && Vector.magnitudeSquared(Vector.sub(this.position, mob[i].position)) < this.alertRange2) { 334 mob[i].locatePlayer(); 335 } 336 } 337 }, 338 alwaysSeePlayer() { 339 if (!m.isCloak) { 340 this.seePlayer.recall = true; 341 this.seePlayer.position.x = player.position.x; 342 this.seePlayer.position.y = player.position.y; 343 } 344 }, 345 seePlayerByHistory(depth = 30) { //depth max 60? limit of history 346 if (!(simulation.cycle % this.seePlayerFreq)) { 347 if (Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 && !m.isCloak) { 348 this.foundPlayer(); 349 } else if (this.seePlayer.recall) { 350 this.lostPlayer(); 351 if (!m.isCloak) { 352 for (let i = 0; i < depth; i++) { //if lost player lock onto a player location in history 353 let history = m.history[(m.cycle - 10 * i) % 600] 354 if (Matter.Query.ray(map, this.position, history.position).length === 0) { 355 this.seePlayer.recall = this.memory + Math.round(this.memory * Math.random()); //cycles before mob falls a sleep 356 this.seePlayer.position.x = history.position.x; 357 this.seePlayer.position.y = history.position.y; 358 this.seePlayer.yes = true; 359 //draw the history location found for testing purposes 360 // ctx.beginPath(); 361 // ctx.moveTo(this.position.x, this.position.y); 362 // ctx.lineTo(history.position.x, history.position.y); 363 // ctx.lineWidth = 5; 364 // ctx.strokeStyle = "#000"; 365 // ctx.stroke(); 366 break 367 } 368 } 369 } 370 } 371 } 372 }, 373 seePlayerCheck() { 374 if (!(simulation.cycle % this.seePlayerFreq)) { 375 if ( 376 this.distanceToPlayer2() < this.seeAtDistance2 && 377 Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 && 378 // Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0 && 379 !m.isCloak 380 ) { 381 this.foundPlayer(); 382 } else if (this.seePlayer.recall) { 383 this.lostPlayer(); 384 } 385 } 386 }, 387 seePlayerCheckByDistance() { 388 if (!(simulation.cycle % this.seePlayerFreq)) { 389 if (this.distanceToPlayer2() < this.seeAtDistance2 && !m.isCloak) { 390 this.foundPlayer(); 391 } else if (this.seePlayer.recall) { 392 this.lostPlayer(); 393 } 394 } 395 }, 396 seePlayerByDistOrLOS() { 397 if (!(simulation.cycle % this.seePlayerFreq)) { 398 if ( 399 (this.distanceToPlayer2() < this.seeAtDistance2 || (Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0)) && //&& Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0 400 !m.isCloak 401 ) { 402 this.foundPlayer(); 403 } else if (this.seePlayer.recall) { 404 this.lostPlayer(); 405 } 406 } 407 }, 408 isLookingAtPlayer(threshold) { 409 const diff = Vector.normalise(Vector.sub(player.position, this.position)); 410 //make a vector for the mob's direction of length 1 411 const dir = { 412 x: Math.cos(this.angle), 413 y: Math.sin(this.angle) 414 }; 415 //the dot product of diff and dir will return how much over lap between the vectors 416 const dot = Vector.dot(dir, diff); 417 // console.log(Math.cos(dot)*180/Math.PI) 418 if (dot > threshold) { 419 return true; 420 } else { 421 return false;
422 } 423 }, 424 lookRange: 0.2 + Math.random() * 0.2, 425 lookTorque: 0.0000004 * (Math.random() > 0.5 ? -1 : 1), 426 seePlayerByLookingAt() { 427 if (!(simulation.cycle % this.seePlayerFreq) && (this.seePlayer.recall || this.isLookingAtPlayer(this.lookRange))) { 428 if ( 429 this.distanceToPlayer2() < this.seeAtDistance2 && 430 Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 && 431 // Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0 && 432 !m.isCloak 433 ) { 434 this.foundPlayer(); 435 } else if (this.seePlayer.recall) { 436 this.lostPlayer(); 437 } 438 } 439 //if you don't recall player location rotate and draw to show where you are looking 440 if (!this.seePlayer.recall) { 441 this.torque = this.lookTorque * this.inertia; 442 //draw 443 const range = Math.PI * this.lookRange; 444 ctx.beginPath(); 445 ctx.arc(this.position.x, this.position.y, this.radius * 2.5, this.angle - range, this.angle + range); 446 ctx.arc(this.position.x, this.position.y, this.radius * 1.4, this.angle + range, this.angle - range, true); 447 ctx.fillStyle = "rgba(0,0,0,0.07)"; 448 ctx.fill(); 449 } 450 }, 451 playerPosRandomY() { 452 return { 453 x: player.position.x, // + (Math.random() - 0.5) * 50, 454 y: player.position.y + (Math.random() - 0.5) * 110 455 }; 456 }, 457 // hacked() { //set this.hackedTarget variable before running this method 458 // //find a new target 459 // if (!(simulation.cycle % this.seePlayerFreq)) { 460 // this.hackedTarget = null 461 // for (let i = 0, len = mob.length; i < len; i++) { 462 // if (mob[i] !== this) { 463 // // const DIST = Vector.magnitude(Vector.sub(this.position, mob[j])); 464 // if (Matter.Query.ray(map, this.position, mob[i].position).length === 0 && 465 // Matter.Query.ray(body, this.position, mob[i].position).length === 0) { 466 // this.hackedTarget = mob[i] 467 // } 468 // } 469 // } 470 // } 471 // //acceleration towards targets 472 // if (this.hackedTarget) { 473 // this.force = Vector.mult(Vector.normalise(Vector.sub(this.hackedTarget.position, this.position)), this.mass * 0.0015) 474 // } 475 // }, 476 harmZone() { 477 if (this.seePlayer.yes) { 478 ctx.setLineDash([125 * Math.random(), 125 * Math.random()]); 479 // ctx.lineDashOffset = 6*(simulation.cycle % 215); 480 if (this.distanceToPlayer() < this.laserRange) { 481 if (m.immuneCycle < m.cycle) { 482 m.damage(0.0003 * simulation.dmgScale); 483 if (m.energy > 0.1) m.energy -= 0.003 484 } 485 ctx.beginPath(); 486 ctx.moveTo(this.position.x, this.position.y); 487 ctx.lineTo(m.pos.x, m.pos.y); 488 ctx.lineTo(m.pos.x + (Math.random() - 0.5) * 3000, m.pos.y + (Math.random() - 0.5) * 3000); 489 ctx.lineWidth = 2; 490 ctx.strokeStyle = "rgb(255,0,170)"; 491 ctx.stroke(); 492 493 ctx.beginPath(); 494 ctx.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI); 495 ctx.fillStyle = "rgba(255,0,170,0.15)"; 496 ctx.fill(); 497 } 498 ctx.beginPath(); 499 ctx.arc(this.position.x, this.position.y, this.laserRange * 0.9, 0, 2 * Math.PI); 500 ctx.strokeStyle = "rgba(255,0,170,0.5)"; 501 ctx.lineWidth = 1; 502 ctx.stroke(); 503 ctx.setLineDash([]); 504 ctx.fillStyle = "rgba(255,0,170,0.03)"; 505 ctx.fill(); 506 } 507 }, 508 laser() { 509 const vertexCollision = function (v1, v1End, domain) { 510 for (let i = 0; i < domain.length; ++i) { 511 let vertices = domain[i].vertices; 512 const len = vertices.length - 1; 513 for (let j = 0; j < len; j++) { 514 results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); 515 if (results.onLine1 && results.onLine2) { 516 const dx = v1.x - results.x; 517 const dy = v1.y - results.y; 518 const dist2 = dx * dx + dy * dy; 519 if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { 520 best = { 521 x: results.x, 522 y: results.y, 523 dist2: dist2, 524 who: domain[i], 525 v1: vertices[j], 526 v2: vertices[j + 1] 527 }; 528 } 529 } 530 } 531 results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); 532 if (results.onLine1 && results.onLine2) { 533 const dx = v1.x - results.x; 534 const dy = v1.y - results.y; 535 const dist2 = dx * dx + dy * dy; 536 if (dist2 < best.dist2) { 537 best = { 538 x: results.x, 539 y: results.y, 540 dist2: dist2, 541 who: domain[i], 542 v1: vertices[0], 543 v2: vertices[len] 544 }; 545 } 546 } 547 } 548 }; 549 if (this.seePlayer.recall && !this.isSlowed) { 550 const seeRange = 2500; 551 best = { 552 x: null, 553 y: null, 554 dist2: Infinity, 555 who: null, 556 v1: null, 557 v2: null 558 }; 559 const look = { 560 x: this.position.x + seeRange * Math.cos(this.angle), 561 y: this.position.y + seeRange * Math.sin(this.angle) 562 }; 563 vertexCollision(this.position, look, map); 564 vertexCollision(this.position, look, body); 565 if (!m.isCloak) vertexCollision(this.position, look, [player]); 566 // hitting player 567 if (best.who === player) { 568 if (m.immuneCycle < m.cycle) { 569 const dmg = 0.0014 * simulation.dmgScale; 570 m.damage(dmg); 571 ctx.fillStyle = "#f00"; //draw damage 572 ctx.beginPath(); 573 ctx.arc(best.x, best.y, dmg * 10000, 0, 2 * Math.PI); 574 ctx.fill(); 575 } 576 } 577 //draw beam 578 if (best.dist2 === Infinity) { 579 best = look; 580 } 581 ctx.beginPath(); 582 ctx.moveTo(this.position.x, this.position.y); 583 ctx.lineTo(best.x, best.y); 584 ctx.strokeStyle = "#f00"; // Purple path 585 ctx.lineWidth = 1; 586 ctx.setLineDash([50 + 120 * Math.random(), 50 * Math.random()]); 587 ctx.stroke(); // Draw it 588 ctx.setLineDash([]); 589 } 590 }, 591 wing(a, radius = 250, ellipticity = 0.4, dmg = 0.0006) {
592 const minorRadius = radius * ellipticity 593 const perp = { x: Math.cos(a), y: Math.sin(a) } // 594 const where = Vector.add(this.position, Vector.mult(perp, radius + 0.8 * this.radius)) 595 596 ctx.beginPath(); 597 ctx.ellipse(where.x, where.y, radius, minorRadius, a, 0, 2 * Math.PI) 598 ctx.fill(); 599 600 //check for wing -> player damage 601 const hitPlayer = Matter.Query.ray([player], this.position, Vector.add(this.position, Vector.mult(perp, radius * 2.05)), minorRadius) 602 if (hitPlayer.length && m.immuneCycle < m.cycle) { 603 m.damage(dmg * simulation.dmgScale); 604 } 605 }, 606 searchSpring() { 607 //draw the two dots on the end of the springs 608 ctx.beginPath(); 609 ctx.arc(this.cons.pointA.x, this.cons.pointA.y, 6, 0, 2 * Math.PI); 610 ctx.arc(this.cons2.pointA.x, this.cons2.pointA.y, 6, 0, 2 * Math.PI); 611 ctx.fillStyle = "#222"; 612 ctx.fill(); 613 614 if (!(simulation.cycle % this.seePlayerFreq)) { 615 if ( 616 (this.seePlayer.recall || this.isLookingAtPlayer(this.lookRange)) && 617 this.distanceToPlayer2() < this.seeAtDistance2 && 618 Matter.Query.ray(map, this.position, player.position).length === 0 && 619 Matter.Query.ray(body, this.position, player.position).length === 0 && 620 !m.isCloak 621 ) { 622 this.foundPlayer(); 623 } else if (this.seePlayer.recall) { 624 this.lostPlayer(); 625 } 626 } 627 }, 628 springAttack() { 629 // set new values of the ends of the spring constraints 630 const stepRange = 600 631 if (this.seePlayer.recall && Matter.Query.ray(map, this.position, this.seePlayer.position).length === 0) { 632 if (!(simulation.cycle % (this.seePlayerFreq * 2))) { 633 const unit = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)) 634 const goal = Vector.add(this.position, Vector.mult(unit, stepRange)) 635 this.springTarget.x = goal.x; 636 this.springTarget.y = goal.y; 637 // this.springTarget.x = this.seePlayer.position.x; 638 // this.springTarget.y = this.seePlayer.position.y; 639 this.cons.length = -200; 640 this.cons2.length = 100 + 1.5 * this.radius; 641 } else if (!(simulation.cycle % this.seePlayerFreq)) { 642 const unit = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)) 643 const goal = Vector.add(this.position, Vector.mult(unit, stepRange)) 644 this.springTarget2.x = goal.x; 645 this.springTarget2.y = goal.y; 646 // this.springTarget2.x = this.seePlayer.position.x; 647 // this.springTarget2.y = this.seePlayer.position.y; 648 this.cons.length = 100 + 1.5 * this.radius; 649 this.cons2.length = -200; 650 } 651 } else { 652 this.torque = this.lookTorque * this.inertia; 653 //draw looking around arcs 654 const range = Math.PI * this.lookRange; 655 ctx.beginPath(); 656 ctx.arc(this.position.x, this.position.y, this.radius * 2.5, this.angle - range, this.angle + range); 657 ctx.arc(this.position.x, this.position.y, this.radius * 1.4, this.angle + range, this.angle - range, true); 658 ctx.fillStyle = "rgba(0,0,0,0.07)"; 659 ctx.fill(); 660 //spring to random place on map 661 const vertexCollision = function (v1, v1End, domain) { 662 for (let i = 0; i < domain.length; ++i) { 663 let vertices = domain[i].vertices; 664 const len = vertices.length - 1; 665 for (let j = 0; j < len; j++) { 666 results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); 667 if (results.onLine1 && results.onLine2) { 668 const dx = v1.x - results.x; 669 const dy = v1.y - results.y; 670 const dist2 = dx * dx + dy * dy; 671 if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { 672 best = { 673 x: results.x, 674 y: results.y, 675 dist2: dist2, 676 who: domain[i], 677 v1: vertices[j], 678 v2: vertices[j + 1] 679 }; 680 } 681 } 682 } 683 results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); 684 if (results.onLine1 && results.onLine2) { 685 const dx = v1.x - results.x; 686 const dy = v1.y - results.y; 687 const dist2 = dx * dx + dy * dy; 688 if (dist2 < best.dist2) { 689 best = { 690 x: results.x, 691 y: results.y, 692 dist2: dist2, 693 who: domain[i], 694 v1: vertices[0], 695 v2: vertices[len] 696 }; 697 } 698 } 699 } 700 }; 701 //move to a random location 702 if (!(simulation.cycle % (this.seePlayerFreq * 4))) { 703 best = { 704 x: null, 705 y: null, 706 dist2: Infinity, 707 who: null, 708 v1: null, 709 v2: null 710 }; 711 const seeRange = 3000; 712 const look = { 713 x: this.position.x + seeRange * Math.cos(this.angle), 714 y: this.position.y + seeRange * Math.sin(this.angle) 715 }; 716 vertexCollision(this.position, look, map); 717 vertexCollision(this.position, look, body); 718 if (best.dist2 != Infinity) { 719 if (Math.random() > 0.5) { 720 this.springTarget.x = best.x; 721 this.springTarget.y = best.y; 722 this.cons.length = 100 + 1.5 * this.radius; 723 this.cons2.length = 100 + 1.5 * this.radius; 724 } else { 725 this.springTarget2.x = best.x; 726 this.springTarget2.y = best.y; 727 this.cons.length = 100 + 1.5 * this.radius; 728 this.cons2.length = 100 + 1.5 * this.radius; 729 } 730 731 732 733 // if (!(simulation.cycle % (this.seePlayerFreq * 2))) { 734 // const unit = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)) 735 // const goal = Vector.add(this.position, Vector.mult(unit, stepRange)) 736 // this.springTarget.x = goal.x; 737 // this.springTarget.y = goal.y; 738 // // this.springTarget.x = this.seePlayer.position.x; 739 // // this.springTarget.y = this.seePlayer.position.y; 740 // this.cons.length = -200; 741 // this.cons2.length = 100 + 1.5 * this.radius; 742 // } else if (!(simulation.cycle % this.seePlayerFreq)) { 743 // const unit = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)) 744 // const goal = Vector.add(this.position, Vector.mult(unit, stepRange)) 745 // this.springTarget2.x = goal.x; 746 // this.springTarget2.y = goal.y; 747 // // this.springTarget2.x = this.seePlayer.position.x; 748 // // this.springTarget2.y = this.seePlayer.position.y; 749 // this.cons.length = 100 + 1.5 * this.radius; 750 // this.cons2.length = -200; 751 // } 752 753 } 754 } 755 } 756 }, 757 curl(range = 1000, mag = -10) { 758 //cause all mobs, and bodies to rotate in a circle 759 applyCurl = function (center, array, isAntiGravity = true) { 760 for (let i = 0; i < array.length; ++i) { 761 if (!array[i].isNotHoldable) { 762 const sub = Vector.sub(center, array[i].position) 763 const radius2 = Vector.magnitudeSquared(sub); 764 765 //if too close, like center mob or shield, don't curl // if too far don't curl 766 if (radius2 < range * range && radius2 > 10000) { 767 const curlVector = Vector.mult(Vector.perp(Vector.normalise(sub)), mag) 768 //apply curl force 769 if (array[i].isMobBullet) { 770 Matter.Body.setVelocity(array[i], { 771 x: array[i].velocity.x * 0.97 + curlVector.x * 0.06, 772 y: array[i].velocity.y * 0.97 + curlVector.y * 0.06 773 }) 774 } else { 775 Matter.Body.setVelocity(array[i], { 776 x: array[i].velocity.x * 0.94 + curlVector.x * 0.06, 777 y: array[i].velocity.y * 0.94 + curlVector.y * 0.06 778 }) 779 } 780 if (isAntiGravity) array[i].force.y -= 0.8 * simulation.g * array[i].mass 781 // //draw curl, for debugging 782 // ctx.beginPath(); 783 // ctx.moveTo(array[i].position.x, array[i].position.y); 784 // ctx.lineTo(array[i].position.x + curlVector.x * 10, array[i].position.y + curlVector.y * 10); 785 // ctx.lineWidth = 2; 786 // ctx.strokeStyle = "#000"; 787 // ctx.stroke(); 788 } 789 } 790 } 791 } 792 applyCurl(this.position, mob, false); 793 applyCurl(this.position, body); 794 applyCurl(this.position, powerUp); 795 // applyCurl(this.position, bullet); // too powerful, just stops all bullets need to write a curl function just for bullets 796 // applyCurl(this.position, [player]); 797 798 //draw limit 799 // ctx.beginPath(); 800 // ctx.arc(this.position.x, this.position.y, range, 0, 2 * Math.PI); 801 // ctx.fillStyle = "rgba(55,255,255, 0.1)"; 802 // ctx.fill(); 803 }, 804 pullPlayer() { 805 if (this.seePlayer.yes && Vector.magnitudeSquared(Vector.sub(this.position, player.position)) < 1000000) { 806 const angle = Math.atan2(player.position.y - this.position.y, player.position.x - this.position.x); 807 player.force.x -= simulation.accelScale * 0.00113 * player.mass * Math.cos(angle) * (m.onGroun
807d ? 2 : 1); 808 player.force.y -= simulation.accelScale * 0.00084 * player.mass * Math.sin(angle); 809 810 ctx.beginPath(); 811 ctx.moveTo(this.position.x, this.position.y); 812 ctx.lineTo(m.pos.x, m.pos.y); 813 ctx.lineWidth = Math.min(60, this.radius * 2); 814 ctx.strokeStyle = "rgba(0,0,0,0.5)"; 815 ctx.stroke(); 816 ctx.beginPath(); 817 ctx.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI); 818 ctx.fillStyle = "rgba(0,0,0,0.3)"; 819 ctx.fill(); 820 } 821 }, 822 repelBullets() { 823 // if (this.seePlayer.yes) { 824 // ctx.lineWidth = "8"; 825 // ctx.strokeStyle = this.fill; 826 // ctx.beginPath(); 827 for (let i = 0, len = bullet.length; i < len; ++i) { 828 const dx = bullet[i].position.x - this.position.x; 829 const dy = bullet[i].position.y - this.position.y; 830 const dist = Math.max(300, Math.sqrt(dx * dx + dy * dy)) 831 if (dist < 700) { 832 ctx.moveTo(this.position.x, this.position.y); 833 ctx.lineTo(bullet[i].position.x, bullet[i].position.y); 834 const angle = Math.atan2(dy, dx); 835 const mag = (500 * bullet[i].mass * simulation.g) / dist; 836 bullet[i].force.x += mag * Math.cos(angle); 837 bullet[i].force.y += mag * Math.sin(angle); 838 } 839 } 840 // ctx.stroke(); 841 // } 842 }, 843 attraction() { 844 //accelerate towards the player 845 if (this.seePlayer.recall) { 846 const force = Vector.mult(Vector.normalise(Vector.sub(this.seePlayer.position, this.position)), this.accelMag * this.mass) 847 this.force.x += force.x; 848 this.force.y += force.y; 849 } 850 }, 851 repulsionRange: 500000, //squared 852 repulsion() { 853 //accelerate towards the player 854 if (this.seePlayer.recall && this.distanceToPlayer2() < this.repulsionRange) { 855 // && dx * dx + dy * dy < 2000000) { 856 const forceMag = this.accelMag * this.mass; 857 const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); 858 this.force.x -= 2 * forceMag * Math.cos(angle); 859 this.force.y -= 2 * forceMag * Math.sin(angle); // - 0.0007 * this.mass; //antigravity 860 } 861 }, 862 hoverOverPlayer() { 863 if (this.seePlayer.recall) { 864 // vertical positioning 865 const rangeY = 250; 866 if (this.position.y > this.seePlayer.position.y - this.hoverElevation + rangeY) { 867 this.force.y -= this.accelMag * this.mass; 868 } else if (this.position.y < this.seePlayer.position.y - this.hoverElevation - rangeY) { 869 this.force.y += this.accelMag * this.mass; 870 } 871 // horizontal positioning 872 const rangeX = 150; 873 if (this.position.x > this.seePlayer.position.x + this.hoverXOff + rangeX) { 874 this.force.x -= this.accelMag * this.mass; 875 } else if (this.position.x < this.seePlayer.position.x + this.hoverXOff - rangeX) { 876 this.force.x += this.accelMag * this.mass; 877 } 878 } 879 }, 880 // invulnerability() { 881 // if (this.isInvulnerable) { 882 // if (this.invulnerabilityCountDown > 0) { 883 // this.invulnerabilityCountDown-- 884 // //graphics //draw a super shield? 885 // ctx.beginPath(); 886 // let vertices = this.vertices;
887 // ctx.moveTo(vertices[0].x, vertices[0].y); 888 // for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); 889 // ctx.lineTo(vertices[0].x, vertices[0].y); 890 // ctx.lineWidth = 20; 891 // // ctx.fillStyle = `rgba(${Math.floor(255 * Math.random())},${Math.floor(255 * Math.random())},${Math.floor(255 * Math.random())},0.5)` 892 // // ctx.fill(); 893 // ctx.strokeStyle = "rgba(255,255,255,0.4)"; 894 // ctx.stroke(); 895 // } else { 896 // this.isInvulnerable = false 897 // this.damageReduction = this.startingDamageReduction 898 // } 899 // } 900 // }, 901 grow() { 902 if (this.seePlayer.recall) { 903 if (this.radius < 80) { 904 const scale = 1.01; 905 Matter.Body.scale(this, scale, scale); 906 this.radius *= scale; 907 // this.torque = -0.00002 * this.inertia; 908 this.fill = `hsl(144, ${this.radius}%, 50%)`; 909 if (this.isShielded) { //remove shield if shielded when growing 910 this.isShielded = false; 911 this.removeConsBB(); 912 } 913 } 914 } else { 915 if (this.radius > 15) { 916 const scale = 0.99; 917 Matter.Body.scale(this, scale, scale); 918 this.radius *= scale; 919 this.fill = `hsl(144, ${this.radius}%, 50%)`; 920 } 921 } 922 }, 923 search() { 924 //be sure to declare searchTarget in mob spawn 925 //accelerate towards the searchTarget 926 if (!this.seePlayer.recall) { 927 const newTarget = function (that) { 928 if (Math.random() < 0.0007) { 929 that.searchTarget = player.position; //chance to target player 930 } else { 931 //target random body 932 that.searchTarget = map[Math.floor(Math.random() * (map.length - 1))].position; 933 } 934 }; 935 936 const sub = Vector.sub(this.searchTarget, this.position); 937 if (Vector.magnitude(sub) > this.radius * 2) { 938 // ctx.beginPath(); 939 // ctx.strokeStyle = "#aaa"; 940 // ctx.moveTo(this.position.x, this.position.y); 941 // ctx.lineTo(this.searchTarget.x,this.searchTarget.y); 942 // ctx.stroke(); 943 //accelerate at 0.1 of normal acceleration 944 this.force = Vector.mult(Vector.normalise(sub), this.accelMag * this.mass * 0.2); 945 } else { 946 //after reaching random target switch to new target 947 newTarget(this); 948 } 949 //switch to a new target after a while 950 if (!(simulation.cycle % (this.seePlayerFreq * 15))) { 951 newTarget(this); 952 } 953 } 954 }, 955 blink() { 956 //teleport towards player as a way to move 957 if (this.seePlayer.recall && !(simulation.cycle % this.blinkRate)) { 958 ctx.beginPath(); 959 ctx.moveTo(this.position.x, this.position.y); 960 const dist = Vector.sub(this.seePlayer.position, this.position); 961 const distMag = Vector.magnitude(dist); 962 const unitVector = Vector.normalise(dist); 963 const rando = (Math.random() - 0.5) * 50; 964 if (distMag < this.blinkLength) { 965 Matter.Body.translate(this, Vector.mult(unitVector, distMag + rando)); 966 } else { 967 Matter.Body.translate(this, Vector.mult(unitVector, this.blinkLength + rando)); 968 } 969 ctx.lineTo(this.position.x, this.position.y);
970 ctx.lineWidth = radius * 2; 971 ctx.strokeStyle = this.stroke; //"rgba(0,0,0,0.5)"; //'#000' 972 ctx.stroke(); 973 } 974 }, 975 drift() { 976 //teleport towards player as a way to move 977 if (this.seePlayer.recall && !(simulation.cycle % this.blinkRate)) { 978 // && !m.lookingAtMob(this,0.5)){ 979 ctx.beginPath(); 980 ctx.moveTo(this.position.x, this.position.y); 981 const dist = Vector.sub(this.seePlayer.position, this.position); 982 const distMag = Vector.magnitude(dist); 983 const vector = Vector.mult(Vector.normalise(dist), this.blinkLength); 984 if (distMag < this.blinkLength) { 985 Matter.Body.setPosition(this, this.seePlayer.position); 986 Matter.Body.translate(this, { 987 x: (Math.random() - 0.5) * 50, 988 y: (Math.random() - 0.5) * 50 989 }); 990 } else { 991 vector.x += (Math.random() - 0.5) * 200; 992 vector.y += (Math.random() - 0.5) * 200; 993 Matter.Body.translate(this, vector); 994 } 995 ctx.lineTo(this.position.x, this.position.y); 996 ctx.lineWidth = radius * 2; 997 ctx.strokeStyle = this.stroke; 998 ctx.stroke(); 999 } 1000 }, 1001 bomb() { 1002 //throw a mob/bullet at player 1003 if ( 1004 !(simulation.cycle % this.fireFreq) && 1005 Math.abs(this.position.x - this.seePlayer.position.x) < 400 && //above player 1006 Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 && //see player 1007 Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0 1008 ) { 1009 spawn.bomb(this.position.x, this.position.y + this.radius * 0.7, 9 + Math.ceil(this.radius / 15), 5); 1010 //add spin and speed 1011 Matter.Body.setAngularVelocity(mob[mob.length - 1], (Math.random() - 0.5) * 0.5); 1012 Matter.Body.setVelocity(mob[mob.length - 1], { 1013 x: this.velocity.x, 1014 y: this.velocity.y 1015 }); 1016 //spin for mob as well 1017 Matter.Body.setAngularVelocity(this, (Math.random() - 0.5) * 0.25); 1018 } 1019 }, 1020 fire() { 1021 const setNoseShape = () => { 1022 const mag = this.radius + this.radius * this.noseLength; 1023 this.vertices[1].x = this.position.x + Math.cos(this.angle) * mag; 1024 this.vertices[1].y = this.position.y + Math.sin(this.angle) * mag; 1025 }; 1026 //throw a mob/bullet at player 1027 if (this.seePlayer.recall) { 1028 //set direction to turn to fire 1029 if (!(simulation.cycle % this.seePlayerFreq)) { 1030 this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); 1031 this.fireDir.y -= Math.abs(this.seePlayer.position.x - this.position.x) / 2500; //gives the bullet an arc //was / 1600 1032 } 1033 //rotate towards fireAngle 1034 const angle = this.angle + Math.PI / 2; 1035 const dot = Vector.dot({ 1036 x: Math.cos(angle), 1037 y: Math.sin(angle) 1038 }, this.fireDir) 1039 // c = Math.cos(angle) * this.fireDir.x + Math.sin(angle) * this.fireDir.y; 1040 const threshold = 0.1; 1041 if (dot > threshold) { 1042 this.torque += 0.000004 * this.inertia; 1043 } else if (dot < -threshold) { 1044 this.torque -= 0.000004 * this.inertia; 1045 } else if (this.noseLength > 1.5 && dot > -0.2 && dot < 0.2) { 1046 //fire 1047 spawn.bullet(this.vertices[1].x, this.vertices[1].y, 9 + Math.ceil(this.radius / 15)); 1048 const v = 15; 1049 Matter.Body.setVelocity(mob[mob.length - 1], { 1050 x: this.velocity.x + this.fireDir.x * v + 3 * Math.random(), 1051 y: this.velocity.y + this.fireDir.y * v + 3 * Math.random() 1052 }); 1053 this.noseLength = 0; 1054 // recoil 1055 this.force.x -= 0.005 * this.fireDir.x * this.mass; 1056 this.force.y -= 0.005 * this.fireDir.y * this.mass; 1057 } 1058 if (this.noseLength < 1.5) this.noseLength += this.fireFreq; 1059 setNoseShape(); 1060 } else if (this.noseLength > 0.1) { 1061 this.noseLength -= this.fireFreq / 2; 1062 setNoseShape(); 1063 } 1064 // else if (this.noseLength < -0.1) { 1065 // this.noseLength += this.fireFreq / 4; 1066 // setNoseShape(); 1067 // } 1068 }, 1069 // launch() { 1070 // if (this.seePlayer.recall) { 1071 // //fire 1072 // spawn.seeker(this.vertices[1].x, this.vertices[1].y, 5 + Math.
1072ceil(this.radius / 15), 5); 1073 // const v = 15; 1074 // Matter.Body.setVelocity(mob[mob.length - 1], { 1075 // x: this.velocity.x + this.fireDir.x * v + Math.random(), 1076 // y: this.velocity.y + this.fireDir.y * v + Math.random() 1077 // }); 1078 // // recoil 1079 // this.force.x -= 0.005 * this.fireDir.x * this.mass; 1080 // this.force.y -= 0.005 * this.fireDir.y * this.mass; 1081 // } 1082 // }, 1083 turnToFacePlayer() { 1084 //turn to face player 1085 const dx = player.position.x - this.position.x; 1086 const dy = -player.position.y + this.position.y; 1087 const dist = this.distanceToPlayer(); 1088 const angle = this.angle + Math.PI / 2; 1089 c = Math.cos(angle) * dx - Math.sin(angle) * dy; 1090 // if (c > 0.04) { 1091 // Matter.Body.rotate(this, 0.01); 1092 // } else if (c < 0.04) { 1093 // Matter.Body.rotate(this, -0.01); 1094 // } 1095 if (c > 0.04 * dist) { 1096 this.torque += 0.002 * this.mass; 1097 } else if (c < 0.04) { 1098 this.torque -= 0.002 * this.mass; 1099 } 1100 }, 1101 facePlayer() { 1102 const unitVector = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); 1103 const angle = Math.atan2(unitVector.y, unitVector.x); 1104 Matter.Body.setAngle(this, angle - Math.PI); 1105 }, 1106 explode(mass = this.mass) { 1107 if (m.immuneCycle < m.cycle) { 1108 m.damage(Math.min(Math.max(0.02 * Math.sqrt(mass), 0.01), 0.35) * simulation.dmgScale); 1109 this.isDropPowerUp = false; 1110 this.death(); //death with no power up or body 1111 } 1112 }, 1113 timeLimit() { 1114 this.timeLeft--; 1115 if (this.timeLeft < 0) { 1116 this.isDropPowerUp = false; 1117 this.death(); //death with no power up 1118 } 1119 }, 1120 healthBar() { //draw health by mob //most health bars are drawn in mobs.healthbar(); 1121 if (this.seePlayer.recall) { 1122 const h = this.radius * 0.3; 1123 const w = this.radius * 2; 1124 const x = this.position.x - w / 2; 1125 const y = this.position.y - w * 0.7; 1126 ctx.fillStyle = "rgba(100, 100, 100, 0.3)"; 1127 ctx.fillRect(x, y, w, h); 1128 ctx.fillStyle = "rgba(255,0,0,0.7)"; 1129 ctx.fillRect(x, y, w * this.health, h); 1130 } 1131 }, 1132 damage(dmg, isBypassShield = false) { 1133 if ((!this.isShielded || isBypassShield) && this.alive) { 1134 if (dmg !== Infinity) { 1135 dmg *= tech.damageFromTech() 1136 //mobs specific damage changes 1137 if (tech.isFarAwayDmg) dmg *= 1 + Math.sqrt(Math.max(500, Math.min(3000, this.distanceToPlayer())) - 500) * 0.0067 //up to 33% dmg at max range of 3000 1138 dmg *= this.damageReduction 1139 //energy and heal drain should be calculated after damage boosts 1140 if (tech.energySiphon && dmg !== Infinity && this.isDropPowerUp && m.immuneCycle < m.cycle) m.energy += Math.min(this.health, dmg) * tech.energySiphon 1141 if (tech.healthDrain && dmg !== Infinity && this.isDropPowerUp && Math.random() < tech.healthDrain * Math.min(this.health, dmg)) { 1142 powerUps.spawn(m.pos.x + 20 * (Math.random() - 0.5), m.pos.y + 20 * (Math.random() - 0.5), "heal"); 1143 } 1144 dmg /= Math.sqrt(this.mass) 1145 } 1146 this.health -= dmg 1147 //this.fill = this.color + this.health + ')'; 1148 this.onDamage(dmg); //custom damage effects 1149 if ((this.health < 0.01 || isNaN(this.health)) && this.alive) this.death(); 1150 } 1151 }, 1152 onDamage() { 1153 // a placeholder for custom effects on mob damage 1154 //to use declare custom method in mob spawn 1155 }, 1156 onDeath() { 1157 // a placeholder for custom effects on mob death 1158 // to use declare custom method in mob spawn 1159 }, 1160 damageReduction: 1, 1161 // damageReductionGoal: 0.001, //must add this to boss set up: me.damageReduction = 0.25 1162 // damageReductionScale: 0.004, //for bosses in this.onDamage determines the impact of dmg on damageReductionGoal 1163 // armor() { //slowly reduce damage reduction, for bosses 1164 // if (this.seePlayer.recall) { 1165 // if (this.damageReductionGoal > 0.24) { 1166 // this.damageReductionGoal = 0.25 1167 // } else { 1168 // this.damageReductionGoal = this.damageReductionGoal * 0.999 + 0.001 * 0.25 //smooth the goal towards 0.25 damage reduction 1169 // } 1170 // this.damageReduction = this.damageReduction * 0.995 + 0.005 * this.damageReductionGoal //smooth damage reduction towards the goal 1171 // // console.log(`damageReduction = ${this.damageReduction.toFixed(4)}`, `damageReductionGoal = ${this.damageReductionGoal.toFixed(4)}`) 1172 // } 1173 // //draw armor 1174 // //draw body 1175 // ctx.beginPath(); 1176 // const vertices = this.vertices;
1177 // ctx.moveTo(vertices[0].x, vertices[0].y); 1178 // for (let j = 1, len = vertices.length; j < len; ++j) { 1179 // ctx.lineTo(vertices[j].x, vertices[j].y); 1180 // } 1181 // ctx.lineTo(vertices[0].x, vertices[0].y); 1182 // console.log(this.damageReduction, this.damageReductionGoal) 1183 // ctx.lineWidth = 3 //60 * (0.25 - this.damageReductionGoal) 1184 // ctx.strokeStyle = `rgba(255,255,255,${4.1*(0.25 - this.damageReductionGoal)})` //"rgba(150,150,225,0.5)"; 1185 // ctx.stroke(); 1186 // }, 1187 leaveBody: true, 1188 isDropPowerUp: true, 1189 death() { 1190 if (tech.collidePowerUps && Math.random() < tech.collidePowerUps && this.isDropPowerUp) powerUps.randomize(this.position) //needs to run before onDeath spawns power ups 1191 this.onDeath(this); //custom death effects 1192 this.removeConsBB(); 1193 this.alive = false; //triggers mob removal in mob[i].replace(i) 1194 1195 if (this.isDropPowerUp) { 1196 if (this.isSoonZombie) { //spawn zombie on death 1197 this.leaveBody = false; 1198 let count = 5 //delay spawn cycles 1199 let cycle = () => { 1200 if (count > 0) { 1201 if (m.alive) requestAnimationFrame(cycle); 1202 if (!simulation.paused && !simulation.isChoosing) { 1203 count-- 1204 } 1205 } else { 1206 spawn.zombie(this.position.x, this.position.y, this.radius, this.vertices.length, this.fill) // zombie(x, y, radius, sides, color) 1207 } 1208 } 1209 requestAnimationFrame(cycle); 1210 } 1211 if (tech.iceIXOnDeath && this.isSlowed) { 1212 for (let i = 0, len = 2 * Math.sqrt(Math.min(this.mass, 25)) * tech.iceIXOnDeath; i < len; i++) b.iceIX(3, Math.random() * 2 * Math.PI, this.position) 1213 } 1214 if (tech.deathSpawnsFromBoss || tech.deathSpawns) { 1215 const spawns = tech.deathSpawns + tech.deathSpawnsFromBoss 1216 const len = Math.min(12, spawns * Math.ceil(Math.random() * simulation.difficulty * spawns)) 1217 for (let i = 0; i < len; i++) { 1218 spawn.spawns(this.position.x + (Math.random() - 0.5) * radius * 2.5, this.position.y + (Math.random() - 0.5) * radius * 2.5); 1219 Matter.Body.setVelocity(mob[mob.length - 1], { 1220 x: this.velocity.x + (Math.random() - 0.5) * 10, 1221 y: this.velocity.x + (Math.random() - 0.5) * 10 1222 }); 1223 } 1224 } 1225 1226 if (tech.deathSkipTime && !m.isBodiesAsleep) { 1227 requestAnimationFrame(() => { 1228 simulation.timePlayerSkip((this.isBoss ? 45 : 25) * tech.deathSkipTime) 1229 simulation.loop(); //ending with a wipe and normal loop fixes some very minor graphical issues where things are draw in the wrong locations 1230 }); //wrapping in animation frame prevents errors, probably 1231 1232 // if (tech.isFlipFlopOn) { 1233 // m.rewind(this.isBoss ? 45 : 25) 1234 // } else { 1235 // requestAnimationFrame(() => { 1236 // simulation.timePlayerSkip(this.isBoss ? 45 : 25) 1237 // simulation.loop(); //ending with a wipe and normal loop fixes some very minor graphical issues where things are draw in the wrong locations 1238 // }); //wrapping in animation frame prevents errors, probably 1239 // } 1240 } 1241 if (tech.isEnergyLoss) m.energy *= 0.8;
1242 powerUps.spawnRandomPowerUp(this.position.x, this.position.y); 1243 m.lastKillCycle = m.cycle; //tracks the last time a kill was made, mostly used in simulation.checks() 1244 mobs.mobDeaths++ 1245 1246 if (Math.random() < tech.sporesOnDeath) { 1247 const amount = Math.min(25, Math.floor(2 + this.mass * (0.5 + 0.5 * Math.random()))) 1248 if (tech.isSporeFlea) { 1249 const len = amount / 2 1250 for (let i = 0; i < len; i++) { 1251 const speed = 10 + 5 * Math.random() 1252 const angle = 2 * Math.PI * Math.random() 1253 b.flea(this.position, { x: speed * Math.cos(angle), y: speed * Math.sin(angle) }) 1254 } 1255 } else if (tech.isSporeWorm) { 1256 const len = amount / 2 1257 for (let i = 0; i < len; i++) b.worm(this.position) 1258 } else { 1259 for (let i = 0; i < amount; i++) b.spore(this.position) 1260 } 1261 } else if (tech.isExplodeMob) { 1262 b.explosion(this.position, Math.min(700, Math.sqrt(this.mass + 6) * (30 + 60 * Math.random()))) 1263 } else if (tech.nailsDeathMob) { 1264 b.targetedNail(this.position, tech.nailsDeathMob, 39 + 6 * Math.random()) 1265 } 1266 if (tech.isBotSpawnerReset) { 1267 for (let i = 0, len = bullet.length; i < len; i++) { 1268 if (bullet[i].botType && bullet[i].endCycle !== Infinity) bullet[i].endCycle = simulation.cycle + 780 //13 seconds 1269 } 1270 } 1271 if (Math.random() < tech.botSpawner) { 1272 b.randomBot(this.position, false) 1273 bullet[bullet.length - 1].endCycle = simulation.cycle + 780 //13 seconds 1274 this.leaveBody = false; // no body since it turned into the bot 1275 } 1276 if (tech.isAddRemoveMaxHealth) { 1277 if (this.isBoss && this.isDropPowerUp) { 1278 powerUps.spawn(this.position.x + 20, this.position.y, "tech", false) 1279 powerUps.spawn(this.position.x - 20, this.position.y, "ammo", false) 1280 powerUps.spawn(this.position.x, this.position.y + 20, "research", false) 1281 powerUps.spawn(this.position.x, this.position.y - 20, "heal", false) 1282 powerUps.spawn(this.position.x - 40, this.position.y, "ammo", false) 1283 powerUps.spawn(this.position.x, this.position.y + 40, "research", false) 1284 powerUps.spawn(this.position.x, this.position.y - 40, "heal", false) 1285 } else { 1286 const amount = 0.005 1287 if (tech.isEnergyHealth) { 1288 if (m.maxEnergy > amount) { 1289 tech.healMaxEnergyBonus -= amount 1290 m.setMaxEnergy(); 1291 } 1292 } else if (m.maxHealth > amount) { 1293 tech.extraMaxHealth -= amount //decrease max health 1294 m.setMaxHealth(); 1295 } 1296 } 1297 } 1298 if (tech.cloakDuplication && !this.isBoss) { 1299 tech.cloakDuplication -= 0.02 1300 powerUps.setPowerUpMode(); //needed after adjusting duplication chance 1301 } 1302 } else if (tech.isShieldAmmo && this.shield && !this.isExtraShield) { 1303 let type = tech.isEnergyNoAmmo ? "heal" : "ammo" 1304 if (Math.random() < 0.4) { 1305 type = "heal" 1306 } else if (Math.random() < 0.3 && !tech.isSuperDeterminism) { 1307 type = "research" 1308 } 1309 for (let i = 0, len = Math.ceil(2 * Math.random()); i < len; i++) {
1310 powerUps.spawn(this.position.x, this.position.y, type); 1311 } 1312 } 1313 if (tech.isRadioactive) { 1314 //look for dots and spread them 1315 let dmgTotal = 0 1316 for (let i = 0, len = this.status.length; i < len; i++) { 1317 if (this.status[i].type === "dot") dmgTotal += this.status[i].dmg * (this.status[i].endCycle - simulation.cycle) 1318 } 1319 if (dmgTotal > 0) { //look for closest mob 1320 let closestRadius = 500; 1321 let closestIndex = null; 1322 for (let i = 0, len = mob.length; i < len; ++i) { 1323 const radius = Vector.magnitude(Vector.sub(this.position, mob[i].position)) 1324 if (mob[i].alive && !mob[i].isShielded && radius < closestRadius) { 1325 closestRadius = radius 1326 closestIndex = i 1327 } 1328 } 1329 if (closestIndex) { 1330 mobs.statusDoT(mob[closestIndex], dmgTotal / 180, 180) 1331 ctx.beginPath(); 1332 ctx.moveTo(this.position.x, this.position.y); 1333 ctx.lineTo(mob[closestIndex].position.x, mob[closestIndex].position.y); 1334 ctx.lineWidth = this.radius; 1335 ctx.strokeStyle = "rgba(0,80,80,1)"; 1336 ctx.stroke(); 1337 } 1338 //draw AOE 1339 // simulation.drawList.push({ //add dmg to draw queue 1340 // x: this.position.x, 1341 // y: this.position.y, 1342 // radius: radius, 1343 // color: "rgba(0,80,80,0.03)", 1344 // time: 15 1345 // }); 1346 } 1347 } 1348 }, 1349 removeConsBB() { 1350 for (let i = 0, len = consBB.length; i < len; ++i) { 1351 if (consBB[i].bodyA === this) { 1352 if (consBB[i].bodyB.shield) { //&& !this.shield 1353 consBB[i].bodyB.do = function () { this.death() } 1354 } 1355 consBB[i].bodyA = consBB[i].bodyB; 1356 consBB.splice(i, 1); 1357 this.removeConsBB(); 1358 break; 1359 } else if (consBB[i].bodyB === this) { 1360 if (consBB[i].bodyA.shield) { 1361 consBB[i].bodyA.do = function () { this.death() } 1362 } 1363 consBB[i].bodyB = consBB[i].bodyA; 1364 consBB.splice(i, 1); 1365 this.removeConsBB(); 1366 break; 1367 } 1368 } 1369 }, 1370 removeCons() { 1371 for (let i = 0, len = cons.length; i < len; ++i) { 1372 if (cons[i].bodyA === this) { 1373 cons[i].bodyA = cons[i].bodyB; 1374 cons.splice(i, 1); 1375 this.removeCons(); 1376 break; 1377 } else if (cons[i].bodyB === this) { 1378 cons[i].bodyB = cons[i].bodyA; 1379 cons.splice(i, 1); 1380 this.removeCons(); 1381 break; 1382 } 1383 } 1384 }, 1385 //replace dead mob with a regular body 1386 replace(i) { 1387 //if there are too many bodies don't turn into blocks to help performance 1388 if (this.leaveBody && body.length < 40 && this.mass < 200 && this.radius > 18) { 1389 let v = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) //might help with vertex collision issue, not sure 1390 if (v.length > 5 && body.length < 35 && Math.random() < 0.25) { 1391 const cutPoint = 3 + Math.floor((v.length - 6) * Math.random()) //Math.floor(v.length / 2) 1392 const v2 = v.slice(0, cutPoint + 1) 1393 v = v.slice(cutPoint - 1) 1394 const len = body.length; 1395 body[len] = Matter.Bodies.fromVertices(this.position.x, this.position.y, v2); 1396 Matter.Body.setVelocity(body[len], Vector.mult(this.velocity, 0.5)); 1397 Matter.Body.setAngularVelocity(body[len], this.angularVelocity); 1398 body[len].collisionFilter.category = cat.body; 1399 body[len].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet; 1400 body[len].classType = "body"; 1401 Composite.add(engine.world, body[len]); //add to world 1402 1403 const len2 = body.length; 1404 body[len2] = Matter.Bodies.fromVertices(this.position.x, this.position.y, v); 1405 Matter.Body.setVelocity(body[len2], Vector.mult(this.velocity, 0.5)); 1406 Matter.Body.setAngularVelocity(body[len2], this.angularVelocity); 1407 body[len2].collisionFilter.category = cat.body; 1408 body[len2].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet; 1409 // if (body[len].mass > 10 || 45 + 10 * Math.random() < body.length) { 1410 // body[len].collisionFilter.mask = cat.player | cat.bullet | cat.mob | cat.mobBullet; 1411 // } 1412 body[len2].classType = "body"; 1413 Composite.add(engine.world, body[len2]); //add to world 1414 1415 //large mobs shrink so they don't block paths 1416 if (body[len].mass + body[len2].mass > 16) { 1417 const massLimit = 8 + 6 * Math.random() 1418 const shrink = function (that1, that2) { 1419 if (that1.mass + that2.mass > massLimit) { 1420 const scale = 0.95; 1421 Matter.Body.scale(that1, scale, scale); 1422 Matter.Body.scale(that2, scale, scale); 1423 setTimeout(shrink, 20, that1, that2); 1424 } 1425 }; 1426 shrink(body[len], body[len2]) 1427 } 1428 } else { 1429 const len = body.length; 1430 body[len] = Matter.Bodies.fromVertices(this.position.x, this.position.y, v); 1431 Matter.Body.setVelocity(body[len], Vector.mult(this.velocity, 0.5)); 1432 Matter.Body.setAngularVelocity(body[len], this.angularVelocity); 1433 body[len].collisionFilter.category = cat.body; 1434 body[len].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet; 1435 body[len].classType = "body"; 1436 Composite.add(engine.world, body[len]); //add to world 1437 1438 //large mobs shrink so they don't block paths 1439 if (body[len].mass > 9) { 1440 const massLimit = 7 + 4 * Math.random() 1441 const shrink = function (that) { 1442 if (that.mass > massLimit) { 1443 const scale = 0.95; 1444 Matter.Body.scale(that, scale, scale); 1445 setTimeout(shrink, 20, that); 1446 } 1447 }; 1448 shrink(body[len]) 1449 } 1450 } 1451 Matter.Composite.remove(engine.world, this); 1452 mob.splice(i, 1); 1453 if (tech.isMobBlockFling) { 1454 const who = body[body.length - 1] 1455 if (!who.isNotHoldable) { 1456 b.targetedBlock(who) 1457 Matter.Body.setAngularVelocity(who, (0.5 + 0.2 * Math.random()) * (Math.random() < 0.5 ? -1 : 1)); 1458 // who.torque += who.inertia * 0.002 * (Math.random() - 0.5) 1459 } 1460 } 1461 } else { 1462 Matter.Composite.remove(engine.world, this); 1463 mob.splice(i, 1); 1464 } 1465 } 1466 }); 1467 mob[i].alertRange2 = Math.pow(mob[i].radius * 3 + 550, 2); 1468 Composite.add(engine.world, mob[i]); //add to world 1469 } 1470};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.