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
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))
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

View File

@@ -58,7 +58,7 @@ const level = {
// 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)

View File

@@ -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();

View File

@@ -8745,7 +8745,7 @@ const tech = {
},
{
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,
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

View File

@@ -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)
stressenergy tensor
radioisotope thermoelectric generator -
retrovirus: these things make JUNK DNA so link it somehow to that tech?