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?