snakeBoss - boss with a tail that grows longer after damage or eating power ups

trying out putting actual system error messages directly into the in-game console

charmed baryons: 0.66->0.8x movement
grappling hook field: 0.6->0.5 damage taken
This commit is contained in:
landgreen
2024-07-06 17:22:58 -07:00
parent 022e2fa80f
commit 20f9b790de
5 changed files with 141 additions and 80 deletions

View File

@@ -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 // 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(`<strong style='color:red;'>ERROR:</strong> ${error.message} <u>${error.filename}:${error.lineno}</u>`)
});
document.getElementById("seed").placeholder = Math.initialSeed = String(Math.floor(Date.now() % 100000)) 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.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 Math.seededRandom = function (min = 0, max = 1) { // in order to work 'Math.seed' must NOT be undefined

View File

@@ -56,9 +56,9 @@ const level = {
// for (let i = 0; i < 1; i++) powerUps.directSpawn(-50, -70, "difficulty", false); // 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 // spawn.mapRect(575, -700, 25, 425); //block mob line of site on testing
// level.testing(); // level.testing();
// for (let i = 0; i < 1; ++i) spawn.snakeBoss(1400, -500) // 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 }); // Matter.Body.setPosition(player, { x: -200, y: -3330 });
// for (let i = 0; i < 4; ++i) spawn.sucker(1300, -500 + 100 * Math.random()) // for (let i = 0; i < 4; ++i) spawn.sucker(1300, -500 + 100 * Math.random())
// spawn.hopper(1900, -500) // spawn.hopper(1900, -500)

View File

@@ -6,8 +6,8 @@ const spawn = {
"orbitalBoss", "historyBoss", "shooterBoss", "cellBossCulture", "bomberBoss", "spiderBoss", "launcherBoss", "laserTargetingBoss", "orbitalBoss", "historyBoss", "shooterBoss", "cellBossCulture", "bomberBoss", "spiderBoss", "launcherBoss", "laserTargetingBoss",
"powerUpBoss", "powerUpBossBaby", "streamBoss", "pulsarBoss", "spawnerBossCulture", "grenadierBoss", "growBossCulture", "blinkBoss", "powerUpBoss", "powerUpBossBaby", "streamBoss", "pulsarBoss", "spawnerBossCulture", "grenadierBoss", "growBossCulture", "blinkBoss",
"snakeSpitBoss", "laserBombingBoss", "blockBoss", "revolutionBoss", "slashBoss", "shieldingBoss", "snakeSpitBoss", "laserBombingBoss", "blockBoss", "revolutionBoss", "slashBoss", "shieldingBoss",
"timeSkipBoss", "dragonFlyBoss", "beetleBoss", "sneakBoss", "mantisBoss", "timeSkipBoss", "dragonFlyBoss", "beetleBoss", "sneakBoss", "mantisBoss", "laserLayerBoss",
"laserLayerBoss" "snakeBoss",
], ],
bossTypeSpawnOrder: [], //preset list of boss names calculated at the start of a run by the randomSeed bossTypeSpawnOrder: [], //preset list of boss names calculated at the start of a run by the randomSeed
bossTypeSpawnIndex: 0, //increases as the boss type cycles bossTypeSpawnIndex: 0, //increases as the boss type cycles
@@ -3870,111 +3870,182 @@ const spawn = {
}; };
}, },
snakeBoss(x, y) { 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]; let me = mob[mob.length - 1];
me.stroke = "transparent"; //used for drawGhost me.stroke = "transparent"; //used for drawGhost
// Matter.Body.setStatic(me, true); //make static, breaks game on player collision Matter.Body.setDensity(me, 0.03); //extra dense //normal is 0.001 //makes effective life much larger
Matter.Body.setDensity(me, 0.002); //extra dense //normal is 0.001 //makes effective life much larger
me.isBoss = true; me.isBoss = true;
me.damageReduction = 0.02 me.damageReduction = 0.6
me.startingDamageReduction = me.damageReduction me.startingDamageReduction = me.damageReduction
me.isInvulnerable = false me.isInvulnerable = false
me.nextHealthThreshold = 0.75 me.nextHealthThreshold = 0.75
me.invulnerableCount = 0 me.invulnerableCount = 0
me.history = [] 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.history.push({ x: me.position.x + i, y: me.position.y })
} }
me.frictionStatic = 0; me.frictionStatic = 0;
me.friction = 0; me.friction = 0;
me.memory = 240 me.memory = 240
me.seePlayerFreq = 55 me.seePlayerFreq = 55
me.delay = 6 + 4 * simulation.CDScale; me.delay = 4 + 2 * simulation.CDScale;//8 + 3 * simulation.CDScale;
me.nextBlinkCycle = me.delay; me.nextBlinkCycle = me.delay;
me.radius = 30 me.JumpDistance = 0//set in redMode()
me.JumpDistance = me.radius * 2
// spawn.shield(me, x, y, 1); // spawn.shield(me, x, y, 1);
me.collisionFilter.mask = cat.bullet | cat.map //| cat.body //cat.player | 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 () { me.onDamage = function () {
if (this.health < this.nextHealthThreshold) { if (this.health < this.nextHealthThreshold) {
this.health = this.nextHealthThreshold - 0.01 this.health = this.nextHealthThreshold - 0.01
this.nextHealthThreshold = Math.floor(this.health * 4) / 4 //0.75,0.5,0.25 this.nextHealthThreshold = Math.floor(this.health * 4) / 4 //0.75,0.5,0.25
this.invulnerableCount = 90 this.invulnerableCount = 300
this.isInvulnerable = true this.isInvulnerable = true
this.damageReduction = 0 this.damageReduction = 0
this.laserDelay = 130 if (this.history.length < 200) for (let i = 0; i < 11; i++) this.history.unshift(this.history[0])
const where = this.history[0] this.blueMode()
for (let i = 0; i < 10; i++) {
this.history.unshift(where)
}
} }
}; };
me.onDeath = function () { me.onDeath = function () {
powerUps.spawnBossPowerUp(this.position.x, this.position.y) 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 () { 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 //reset tail length for a sec to prevent repeat damage
for (let i = 0; i < this.history.length - 1; i++) { for (let i = 0, len = this.history.length; i < len; i++) {
if (Matter.Query.ray([player], this.history[i], this.history[i + 1], 10).length > 0) { this.history[i] = { x: this.position.x, y: this.position.y }
m.damage(0.004 * simulation.dmgScale); }
simulation.drawList.push({ //add dmg to draw queue break
x: m.pos.x, }
y: m.pos.y,
radius: 30,
color: color,
time: 10
});
break
} }
} }
if (this.nextBlinkCycle < simulation.cycle) { //teleport towards the player if (this.nextBlinkCycle < simulation.cycle) { //teleport towards the player
this.nextBlinkCycle = simulation.cycle + this.delay; 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 //custom see player by history code
let move = (target = this.seePlayer.position) => { let move = (target = this.seePlayer.position) => {
const dist = Vector.sub(target, this.position); const dist = Vector.sub(target, this.position);
Matter.Body.translate(this, Vector.mult(Vector.normalise(dist), this.JumpDistance)); Matter.Body.translate(this, Vector.mult(Vector.normalise(dist), this.JumpDistance));
Matter.Body.setVelocity(this, { x: 0, y: 0 }); Matter.Body.setVelocity(this, { x: 0, y: 0 });
Matter.Body.setAngle(this, 0); // Matter.Body.setAngle(this, 0);
Matter.Body.setAngularVelocity(this, 0) Matter.Body.setAngularVelocity(this, 0)
//track previous locations for the tail //track previous locations for the tail
this.history.push({ x: this.position.x, y: this.position.y }) //add newest to end this.history.push({ x: this.position.x, y: this.position.y }) //add newest to end
this.history.shift() //remove first (oldest) 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 = { let close = {
dist2: Infinity, dist: Infinity,
targetPos: null targetPos: null,
index: null,
} }
for (let i = 0; i < powerUp.length; i++) { for (let i = 0; i < powerUp.length; i++) {
if (Matter.Query.ray(map, this.position, powerUp[i].position).length === 0) { if (Matter.Query.ray(map, this.position, powerUp[i].position).length === 0) {
const dist = Vector.magnitudeSquared(Vector.sub(this.position, powerUp[i].position)) const dist = Vector.magnitude(Vector.sub(this.position, powerUp[i].position))
if (dist < close.dist2) { if (dist < close.dist) {
close = { close = {
dist2: dist, dist: dist,
// targetPos: { x: powerUp[i].position.x, y: powerUp[i].position.y } target: powerUp[i],
target: powerUp[i] index: i,
} }
} }
} }
} }
if (close.dist2 < 5000 * 5000) { if (close.dist < 3000) { //chase power ups if they are near
//chase power ups
move(close.target.position) move(close.target.position)
//check if close to power up and eat it //check if close to power up and eat it
if (close.dist2 < 200 * 200) { if (close.dist < this.JumpDistance + 2 * this.radius) {
//eat power up 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.seePlayer.yes = true;
this.locatePlayer(); this.locatePlayer();
if (!this.seePlayer.yes) this.seePlayer.yes = true; if (!this.seePlayer.yes) this.seePlayer.yes = true;
move() move()
} else if (this.seePlayer.recall) { } else if (this.seePlayer.recall) { //chase player's history
this.lostPlayer(); this.lostPlayer();
if (!m.isCloak) { if (!m.isCloak) {
for (let i = 0; i < 50; i++) { //if lost player lock onto a player location in history 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) { if (this.invulnerableCount < 0) {
this.isInvulnerable = false this.isInvulnerable = false
this.damageReduction = this.startingDamageReduction this.damageReduction = this.startingDamageReduction
this.redMode()
} }
//draw invulnerable //draw invulnerable
ctx.beginPath(); ctx.beginPath();

View File

@@ -8745,7 +8745,7 @@ const tech = {
}, },
{ {
name: "charmed baryons", name: "charmed baryons",
description: `<strong>0.66x</strong> <strong>movement</strong> and <strong>jumping</strong><br><strong class='color-worm'>wormholes</strong> cost <strong>zero</strong> <strong class='color-f'>energy</strong>`, description: `<strong>0.8x</strong> <strong>movement</strong> and <strong>jumping</strong><br><strong class='color-worm'>wormholes</strong> cost <strong>zero</strong> <strong class='color-f'>energy</strong>`,
isFieldTech: true, isFieldTech: true,
maxCount: 1, maxCount: 1,
count: 0, count: 0,
@@ -8757,8 +8757,8 @@ const tech = {
requires: "wormhole, not affine connection", requires: "wormhole, not affine connection",
effect() { effect() {
tech.isFreeWormHole = true tech.isFreeWormHole = true
tech.baseFx *= 0.66 tech.baseFx *= 0.8
tech.baseJumpForce *= 0.66 tech.baseJumpForce *= 0.8
m.setMovement() m.setMovement()
}, },
//also removed in m.setHoldDefaults() if player switches into a bad field //also removed in m.setHoldDefaults() if player switches into a bad field

View File

@@ -1,29 +1,19 @@
******************************************************** NEXT PATCH ************************************************** ******************************************************** NEXT PATCH **************************************************
renamed MACHO -> dark matter snakeBoss - boss with a tail that grows longer after damage or eating power ups
tech: MACHO - dark matter is active when you are outside not inside it's range, 1.5 to dark matter effects maybe have it eat blocks too?
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
boss health nerf: almost every boss has ~0.8x less health trying out putting actual system error messages directly into the in-game console
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
several bug fixes charmed baryons: 0.66->0.8x movement
grappling hook field: 0.6->0.5 damage taken
******************************************************* DESIGN ****************************************************** ******************************************************* DESIGN ******************************************************
priorities priorities
synergies between tech synergies between tech
difficult to achieve synergies that feel so powerful they are game breaking / changing 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 bosses, mobs, levels, tech
graphical indicators of tech effects and quantity graphical indicators of tech effects and quantity
subtle lore woven into unexpected places subtle lore woven into unexpected places
@@ -44,19 +34,11 @@ list of powerful synergies
*********************************************************** TODO ***************************************************** *********************************************************** TODO *****************************************************
snakeBoss - boss with a tail that grows longer merge multiple power ups of the same type if nearby
improve behavior for when it can't see player 5-10 ammo, research, coupling can merge to form a slightly larger power up version
wander around looking for power ups check for merger possibility every 60 seconds?
what if it gets lost? adjust mass spawns to just spawn larger power ups versions and change?
eat power ups spawnDelay
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
white laser white laser
what to name? not much in wikipedia 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? do damage?
plasma torch tech? 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? make some explosions have less knock back?
annoying with flame test, boom bot? annoying with flame test, boom bot?
@@ -1282,7 +1267,7 @@ possible names for tech
Josephson junction - superconducting junction Josephson junction - superconducting junction
Pyroelectricity - voltage from temp changes - upgrade from piezoelectricity Pyroelectricity - voltage from temp changes - upgrade from piezoelectricity
Unruh effect - accelerating makes heat/thermal particles 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)
stressenergy tensor stressenergy tensor
radioisotope thermoelectric generator - radioisotope thermoelectric generator -
retrovirus: these things make JUNK DNA so link it somehow to that tech? retrovirus: these things make JUNK DNA so link it somehow to that tech?