diff --git a/js/index.js b/js/index.js index 8b3213d..3dbdae8 100644 --- a/js/index.js +++ b/js/index.js @@ -12,6 +12,10 @@ Math.hash = s => { // document.getElementById("seed").placeholder = Math.initialSeed = Math.floor(Date.now() % 100000) //random every time: just the time in milliseconds UTC +window.addEventListener('error', error => { + simulation.makeTextLog(`ERROR: ${error.message} ${error.filename}:${error.lineno}`) +}); + document.getElementById("seed").placeholder = Math.initialSeed = String(Math.floor(Date.now() % 100000)) Math.seed = Math.abs(Math.hash(Math.initialSeed)) //update randomizer seed in case the player changed it Math.seededRandom = function (min = 0, max = 1) { // in order to work 'Math.seed' must NOT be undefined diff --git a/js/level.js b/js/level.js index 931d416..cfdbfe7 100644 --- a/js/level.js +++ b/js/level.js @@ -56,9 +56,9 @@ const level = { // for (let i = 0; i < 1; i++) powerUps.directSpawn(-50, -70, "difficulty", false); // spawn.mapRect(575, -700, 25, 425); //block mob line of site on testing // level.testing(); - + // for (let i = 0; i < 1; ++i) spawn.snakeBoss(1400, -500) - // for (let i = 0; i < 2; i++) powerUps.directSpawn(800, -100, "coupling"); + // for (let i = 0; i < 2; i++) powerUps.directSpawn(800, -100, "coupling"); // Matter.Body.setPosition(player, { x: -200, y: -3330 }); // for (let i = 0; i < 4; ++i) spawn.sucker(1300, -500 + 100 * Math.random()) // spawn.hopper(1900, -500) diff --git a/js/spawn.js b/js/spawn.js index b771cc4..6706fb1 100644 --- a/js/spawn.js +++ b/js/spawn.js @@ -6,8 +6,8 @@ const spawn = { "orbitalBoss", "historyBoss", "shooterBoss", "cellBossCulture", "bomberBoss", "spiderBoss", "launcherBoss", "laserTargetingBoss", "powerUpBoss", "powerUpBossBaby", "streamBoss", "pulsarBoss", "spawnerBossCulture", "grenadierBoss", "growBossCulture", "blinkBoss", "snakeSpitBoss", "laserBombingBoss", "blockBoss", "revolutionBoss", "slashBoss", "shieldingBoss", - "timeSkipBoss", "dragonFlyBoss", "beetleBoss", "sneakBoss", "mantisBoss", - "laserLayerBoss" + "timeSkipBoss", "dragonFlyBoss", "beetleBoss", "sneakBoss", "mantisBoss", "laserLayerBoss", + "snakeBoss", ], bossTypeSpawnOrder: [], //preset list of boss names calculated at the start of a run by the randomSeed bossTypeSpawnIndex: 0, //increases as the boss type cycles @@ -3870,111 +3870,182 @@ const spawn = { }; }, snakeBoss(x, y) { - mobs.spawn(x, y, 0, 30, `rgba(255,0,200)`); //"rgb(221,102,119)" + mobs.spawn(x, y, 0, 25, `rgba(255,0,200)`); //"rgb(221,102,119)" let me = mob[mob.length - 1]; me.stroke = "transparent"; //used for drawGhost - // Matter.Body.setStatic(me, true); //make static, breaks game on player collision - Matter.Body.setDensity(me, 0.002); //extra dense //normal is 0.001 //makes effective life much larger + Matter.Body.setDensity(me, 0.03); //extra dense //normal is 0.001 //makes effective life much larger me.isBoss = true; - me.damageReduction = 0.02 + me.damageReduction = 0.6 me.startingDamageReduction = me.damageReduction me.isInvulnerable = false me.nextHealthThreshold = 0.75 me.invulnerableCount = 0 - me.history = [] - for (let i = 0; i < 15; i++) { + for (let i = 0; i < 20; i++) { me.history.push({ x: me.position.x + i, y: me.position.y }) } me.frictionStatic = 0; me.friction = 0; me.memory = 240 me.seePlayerFreq = 55 - me.delay = 6 + 4 * simulation.CDScale; + me.delay = 4 + 2 * simulation.CDScale;//8 + 3 * simulation.CDScale; me.nextBlinkCycle = me.delay; - me.radius = 30 - me.JumpDistance = me.radius * 2 + me.JumpDistance = 0//set in redMode() // spawn.shield(me, x, y, 1); me.collisionFilter.mask = cat.bullet | cat.map //| cat.body //cat.player | + me.powerUpNames = [] + me.redMode = function () { + this.color = `rgba(255,0,200,` + this.fill = this.color + '1)' + this.JumpDistance = 20 + let cycle = () => { + if (this.radius < 25) { + if (m.alive && this.JumpDistance === 20) requestAnimationFrame(cycle); + if (!simulation.paused && !simulation.isChoosing) { + const scale = 1.01; + Matter.Body.scale(this, scale, scale); + this.radius *= scale; + } + } + } + requestAnimationFrame(cycle); + } + me.redMode(); + me.blueMode = function () { + this.color = `rgba(0,0,255,`//`rgba(255,0,200,` + this.fill = this.color + '1)' + this.JumpDistance = 37 //adjust this number in the IF below + let cycle = () => { + if (this.radius > 14) { + if (m.alive && this.JumpDistance === 37) requestAnimationFrame(cycle); + if (!simulation.paused && !simulation.isChoosing) { + const scale = 0.96; + Matter.Body.scale(this, scale, scale); + this.radius *= scale; + } + } + } + requestAnimationFrame(cycle); + } me.onDamage = function () { if (this.health < this.nextHealthThreshold) { this.health = this.nextHealthThreshold - 0.01 this.nextHealthThreshold = Math.floor(this.health * 4) / 4 //0.75,0.5,0.25 - this.invulnerableCount = 90 + this.invulnerableCount = 300 this.isInvulnerable = true this.damageReduction = 0 - this.laserDelay = 130 - const where = this.history[0] - for (let i = 0; i < 10; i++) { - this.history.unshift(where) - } + if (this.history.length < 200) for (let i = 0; i < 11; i++) this.history.unshift(this.history[0]) + this.blueMode() } }; me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) + + //respawn all eaten power ups + let i = 0 + let cycle = () => { + if (i < this.powerUpNames.length) { + if (m.alive) requestAnimationFrame(cycle); + if (!simulation.paused && !simulation.isChoosing && powerUp.length < 300) { + const index = Math.floor(Math.random() * this.history.length) //random segment of tail + const where = { x: this.history[index].x + 25 * (Math.random() - 0.5), y: this.history[index].y + 25 * (Math.random() - 0.5) } + powerUps.spawn(where.x, where.y, this.powerUpNames[i]); + i++ + } + } + } + requestAnimationFrame(cycle); } me.do = function () { - const color = `rgba(255,0,200,${0.3 + 0.07 * Math.random()})` + const color = this.color + (0.35 + 0.25 * Math.random()) + ')' + //check for player collisions in between each segment + if (m.immuneCycle < m.cycle) { + for (let i = 0; i < this.history.length - 1; i++) { + if (Matter.Query.ray([player], this.history[i], this.history[i + 1], 10).length > 0) { + m.immuneCycle = m.cycle + m.collisionImmuneCycles + 60 + const dmg = 0.25 * simulation.dmgScale + m.damage(dmg); + simulation.drawList.push({ //add dmg to draw queue + x: m.pos.x, + y: m.pos.y, + radius: dmg * 1500,//30, + color: color, + time: 20 + }); - //check for player in between each segment - for (let i = 0; i < this.history.length - 1; i++) { - if (Matter.Query.ray([player], this.history[i], this.history[i + 1], 10).length > 0) { - m.damage(0.004 * simulation.dmgScale); - simulation.drawList.push({ //add dmg to draw queue - x: m.pos.x, - y: m.pos.y, - radius: 30, - color: color, - time: 10 - }); - break + //reset tail length for a sec to prevent repeat damage + for (let i = 0, len = this.history.length; i < len; i++) { + this.history[i] = { x: this.position.x, y: this.position.y } + } + break + } } } if (this.nextBlinkCycle < simulation.cycle) { //teleport towards the player this.nextBlinkCycle = simulation.cycle + this.delay; + if (this.isSlowed) this.nextBlinkCycle += this.delay + if (this.isStunned) this.nextBlinkCycle += this.delay * 3 + //custom see player by history code let move = (target = this.seePlayer.position) => { const dist = Vector.sub(target, this.position); + Matter.Body.translate(this, Vector.mult(Vector.normalise(dist), this.JumpDistance)); Matter.Body.setVelocity(this, { x: 0, y: 0 }); - Matter.Body.setAngle(this, 0); + // Matter.Body.setAngle(this, 0); Matter.Body.setAngularVelocity(this, 0) //track previous locations for the tail this.history.push({ x: this.position.x, y: this.position.y }) //add newest to end this.history.shift() //remove first (oldest) } - //look for close powers up in line of sight + //look for close power ups in line of sight let close = { - dist2: Infinity, - targetPos: null + dist: Infinity, + targetPos: null, + index: null, } for (let i = 0; i < powerUp.length; i++) { if (Matter.Query.ray(map, this.position, powerUp[i].position).length === 0) { - const dist = Vector.magnitudeSquared(Vector.sub(this.position, powerUp[i].position)) - if (dist < close.dist2) { + const dist = Vector.magnitude(Vector.sub(this.position, powerUp[i].position)) + if (dist < close.dist) { close = { - dist2: dist, - // targetPos: { x: powerUp[i].position.x, y: powerUp[i].position.y } - target: powerUp[i] + dist: dist, + target: powerUp[i], + index: i, } } } } - if (close.dist2 < 5000 * 5000) { - //chase power ups + if (close.dist < 3000) { //chase power ups if they are near move(close.target.position) + //check if close to power up and eat it - if (close.dist2 < 200 * 200) { - //eat power up + if (close.dist < this.JumpDistance + 2 * this.radius) { + this.powerUpNames.push(close.target.name) //save name to return power ups after this mob dies + Matter.Composite.remove(engine.world, close.target); + powerUp.splice(close.index, 1); + this.health = 1 //heal to full + //add more segments to tail + if (this.history.length < 200) for (let i = 0; i < 4; i++) this.history.unshift(this.history[0]) + //draw pickup for a single cycle + ctx.beginPath(); + ctx.moveTo(this.position.x, this.position.y); + ctx.lineTo(close.target.position.x, close.target.position.y); + ctx.strokeStyle = "#000" + ctx.lineWidth = 4 + ctx.stroke(); } - } else if (Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 && !m.isCloak) { + //go eat blocks to heal? + // } else if (this.health < 0.6) { + + } else if (Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 && !m.isCloak) { //chase player this.seePlayer.yes = true; this.locatePlayer(); if (!this.seePlayer.yes) this.seePlayer.yes = true; move() - } else if (this.seePlayer.recall) { + } else if (this.seePlayer.recall) { //chase player's history this.lostPlayer(); if (!m.isCloak) { for (let i = 0; i < 50; i++) { //if lost player lock onto a player location in history @@ -3998,6 +4069,7 @@ const spawn = { if (this.invulnerableCount < 0) { this.isInvulnerable = false this.damageReduction = this.startingDamageReduction + this.redMode() } //draw invulnerable ctx.beginPath(); diff --git a/js/tech.js b/js/tech.js index 0879a0b..3f2953c 100644 --- a/js/tech.js +++ b/js/tech.js @@ -8745,7 +8745,7 @@ const tech = { }, { name: "charmed baryons", - description: `0.66x movement and jumping
wormholes cost zero energy`, + description: `0.8x movement and jumping
wormholes cost zero energy`, isFieldTech: true, maxCount: 1, count: 0, @@ -8757,8 +8757,8 @@ const tech = { requires: "wormhole, not affine connection", effect() { tech.isFreeWormHole = true - tech.baseFx *= 0.66 - tech.baseJumpForce *= 0.66 + tech.baseFx *= 0.8 + tech.baseJumpForce *= 0.8 m.setMovement() }, //also removed in m.setHoldDefaults() if player switches into a bad field diff --git a/todo.txt b/todo.txt index bf37b72..34a2108 100644 --- a/todo.txt +++ b/todo.txt @@ -1,29 +1,19 @@ ******************************************************** NEXT PATCH ************************************************** -renamed MACHO -> dark matter -tech: MACHO - dark matter is active when you are outside not inside it's range, 1.5 to dark matter effects -tech: dark energy - inside dark matter regen 10 energy -tech: stability - 0.3x damage taken if health equals maxHealth -tech: instability - 2x damage if damage taken is 1x -tech: control theory - 1.5x damage if health equals maxHealth -tech: inertial confinement - while charging tokamak you can fly, but energy drains -tech: stellarator - after firing a block with tokamak, spawn up to 5 heals +snakeBoss - boss with a tail that grows longer after damage or eating power ups + maybe have it eat blocks too? -boss health nerf: almost every boss has ~0.8x less health - secondary bosses also spawn 2 ammo -aerostat 0.85->0.9 damage on the ground -Pauli exclusion 6->8 seconds of invulnerable after getting hit -Gibbs free energy 2->0 research cost, 1.01->1.05 damage scales with energy below 100->maxEnergy -cache 15->17x ammo +trying out putting actual system error messages directly into the in-game console -several bug fixes +charmed baryons: 0.66->0.8x movement +grappling hook field: 0.6->0.5 damage taken ******************************************************* DESIGN ****************************************************** priorities synergies between tech difficult to achieve synergies that feel so powerful they are game breaking / changing - randomized content adds repeatability + randomized content that adds repeatability bosses, mobs, levels, tech graphical indicators of tech effects and quantity subtle lore woven into unexpected places @@ -44,19 +34,11 @@ list of powerful synergies *********************************************************** TODO ***************************************************** -snakeBoss - boss with a tail that grows longer - improve behavior for when it can't see player - wander around looking for power ups - what if it gets lost? - eat power ups - eject them after you die - get longer after eating - eat mobs? - eat blocks? - modes: recolor tail based on modes - hunting power ups -> small + fast : blue cyan - hunting player -> attack? : red/pink - slow high defense : white +merge multiple power ups of the same type if nearby + 5-10 ammo, research, coupling can merge to form a slightly larger power up version + check for merger possibility every 60 seconds? + adjust mass spawns to just spawn larger power ups versions and change? + spawnDelay white laser what to name? not much in wikipedia @@ -71,6 +53,9 @@ tech: atomic pile - lose 1 health if you are above the maximum energy do damage? plasma torch tech? +field tech: molecular assembler - every time you spawn a drone/spore/... become immune to damage for time + scales with how much energy was used to spawn drone/... + make some explosions have less knock back? annoying with flame test, boom bot? @@ -1282,7 +1267,7 @@ possible names for tech Josephson junction - superconducting junction Pyroelectricity - voltage from temp changes - upgrade from piezoelectricity Unruh effect - accelerating makes heat/thermal particles - configuration space - holds the position of everything + configuration space - holds the position of everything (related to fermions/bosons and particle interactions) stress–energy tensor radioisotope thermoelectric generator - retrovirus: these things make JUNK DNA so link it somehow to that tech?