seeded random
all runs are now seeded
seed defaults to the last 8 digits of UTC time in milliseconds
set a custom randomization seed in settings
seed controls:
at start - boss list, mob type list, level list, horizontal flip
during run - tech, gun, field choices, some custom level randomization
doesn't control: mob spawns, mob size, some minor level differences, specific level boss choices, ammo reward values, specific tech effects
bug fix with ground state
This commit is contained in:
@@ -90,6 +90,11 @@
|
||||
<details>
|
||||
<summary>settings</summary>
|
||||
<div style="line-height: 150%;" class="details-div">
|
||||
|
||||
<label for="seed">randomization seed:</label>
|
||||
<input type="text" id="seed" name="seed" autocomplete="off" spellcheck="false" minlength="1" size="20" style="width: 120px;">
|
||||
<br>
|
||||
|
||||
<label for="difficulty-select" title="effects: number of mobs, damage done by mobs, damage done to mobs, mob speed, heal effects">combat difficulty:</label>
|
||||
<select name="difficulty-select" id="difficulty-select" style="background-color: #fff">
|
||||
<option value="1">easy</option>
|
||||
|
||||
52
js/index.js
52
js/index.js
@@ -1,4 +1,39 @@
|
||||
"use strict";
|
||||
|
||||
//convert text into numbers for seed
|
||||
Math.hash = s => { for (var i = 0, h = 9; i < s.length;) h = Math.imul(h ^ s.charCodeAt(i++), 9 ** 9); return h ^ h >>> 9 }
|
||||
|
||||
// const date1 = new Date()
|
||||
// Math.seed = date1.getUTCDate() * date1.getUTCFullYear(); // daily seed, day + year
|
||||
// Math.seed = Date.now() //random every time: just the time in seconds UTC
|
||||
Math.seed = Math.floor(Date.now() % 100000000) //random every time: just the time in seconds UTC
|
||||
Math.seededRandom = function(min = 0, max = 1) { // in order to work 'Math.seed' must NOT be undefined
|
||||
Math.seed = (Math.seed * 9301 + 49297) % 233280;
|
||||
return min + Math.seed / 233280 * (max - min);
|
||||
}
|
||||
document.getElementById("seed").placeholder = Math.seed //display seed in settings
|
||||
//Math.seed is set to document.getElementById("seed").value in level.populate level at the start of runs
|
||||
// console.log(Math.seed)
|
||||
|
||||
|
||||
function shuffle(array) {
|
||||
var currentIndex = array.length,
|
||||
temporaryValue,
|
||||
randomIndex;
|
||||
// While there remain elements to shuffle...
|
||||
while (0 !== currentIndex) {
|
||||
// Pick a remaining element...
|
||||
// randomIndex = Math.floor(Math.random() * currentIndex);
|
||||
randomIndex = Math.floor(Math.seededRandom(0, currentIndex)) //Math.floor(Math.random() * currentIndex);
|
||||
currentIndex -= 1;
|
||||
// And swap it with the current element.
|
||||
temporaryValue = array[currentIndex];
|
||||
array[currentIndex] = array[randomIndex];
|
||||
array[randomIndex] = temporaryValue;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
//collision groups
|
||||
// cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet | cat.mobShield | cat.phased
|
||||
const cat = {
|
||||
@@ -36,23 +71,6 @@ const color = { //light
|
||||
// map: "#444",
|
||||
// }
|
||||
|
||||
function shuffle(array) {
|
||||
var currentIndex = array.length,
|
||||
temporaryValue,
|
||||
randomIndex;
|
||||
// While there remain elements to shuffle...
|
||||
while (0 !== currentIndex) {
|
||||
// Pick a remaining element...
|
||||
randomIndex = Math.floor(Math.random() * currentIndex);
|
||||
currentIndex -= 1;
|
||||
// And swap it with the current element.
|
||||
temporaryValue = array[currentIndex];
|
||||
array[currentIndex] = array[randomIndex];
|
||||
array[randomIndex] = temporaryValue;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
// shrink power up selection menu
|
||||
// if (screen.height < 800) {
|
||||
// document.getElementById("choose-grid").style.fontSize = "1em"; //1.3em is normal
|
||||
|
||||
17
js/level.js
17
js/level.js
@@ -16,7 +16,6 @@ const level = {
|
||||
if (level.levelsCleared === 0) { //this code only runs on the first level
|
||||
// m.immuneCycle = Infinity //you can't take damage
|
||||
// localSettings.levelsClearedLastGame = 10
|
||||
// spawn.setSpawnList();spawn.setSpawnList();
|
||||
// level.difficultyIncrease(30) //30 is near max on hard //60 is near max on why
|
||||
// simulation.isHorizontalFlipped = true
|
||||
// m.setField("standing wave")
|
||||
@@ -222,10 +221,12 @@ const level = {
|
||||
}
|
||||
},
|
||||
populateLevels() {
|
||||
if (document.getElementById("seed").value) Math.seed = Math.hash(document.getElementById("seed").value) //update randomizer seed in case the player changed it
|
||||
|
||||
if (simulation.isTraining) {
|
||||
level.levels = level.trainingLevels.slice(0) //copy array, not by just by assignment
|
||||
} else {
|
||||
simulation.isHorizontalFlipped = (Math.random() < 0.5) ? true : false //if true, some maps are flipped horizontally
|
||||
simulation.isHorizontalFlipped = (Math.seededRandom() < 0.5) ? true : false //if true, some maps are flipped horizontally
|
||||
level.levels = level.playableLevels.slice(0) //copy array, not by just by assignment
|
||||
if (simulation.isCommunityMaps) {
|
||||
// level.levels.push(level.communityLevels)
|
||||
@@ -235,8 +236,9 @@ const level = {
|
||||
} else {
|
||||
level.levels = shuffle(level.levels); //shuffles order of maps
|
||||
}
|
||||
level.levels.splice(Math.floor(level.levels.length * (0.4 + 0.6 * Math.random())), 0, "reservoir"); //add level to the back half of the randomized levels list
|
||||
level.levels.splice(Math.floor(level.levels.length * (0.4 + 0.6 * Math.random())), 0, "reactor"); //add level to the back half of the randomized levels list
|
||||
// level.levels.splice(Math.floor(level.levels.length * (0.4 + 0.6 * Math.random())), 0, "reservoir"); //add level to the back half of the randomized levels list
|
||||
level.levels.splice(Math.floor(Math.seededRandom((level.levels.length) * 0.4, level.levels.length)), 0, "reservoir"); //add level to the back half of the randomized levels list
|
||||
level.levels.splice(Math.floor(Math.seededRandom((level.levels.length) * 0.4, level.levels.length)), 0, "reactor"); //add level to the back half of the randomized levels list
|
||||
level.levels.splice(0, 2); //remove 2 levels from the start of the array
|
||||
if (!build.isExperimentSelection || (build.hasExperimentalMode && !simulation.isCheating)) { //experimental mode is endless, unless you only have an experiment Tech
|
||||
level.levels.unshift("intro"); //add level to the start of the randomized levels list
|
||||
@@ -244,6 +246,9 @@ const level = {
|
||||
level.levels.push("final"); //add level to the end of the randomized levels list
|
||||
}
|
||||
}
|
||||
//set seeded random lists of mobs and bosses
|
||||
for (let i = 0; i < level.levels.length; i++) spawn.mobTypeSpawnOrder.push(spawn.fullPickList[Math.floor(Math.seededRandom(0, spawn.fullPickList.length))])
|
||||
for (let i = 0; i < level.levels.length * 2; i++) spawn.bossTypeSpawnOrder.push(spawn.randomBossList[Math.floor(Math.seededRandom(0, spawn.randomBossList.length))])
|
||||
},
|
||||
flipHorizontal() {
|
||||
const flipX = (who) => {
|
||||
@@ -2508,8 +2513,6 @@ const level = {
|
||||
level.exit.y = -430;
|
||||
|
||||
// level.difficultyIncrease(14); //hard mode level 7
|
||||
spawn.setSpawnList();
|
||||
spawn.setSpawnList();
|
||||
level.defaultZoom = 1500
|
||||
simulation.zoomTransition(level.defaultZoom)
|
||||
document.body.style.backgroundColor = color.background //"#ddd";
|
||||
@@ -3232,8 +3235,6 @@ const level = {
|
||||
|
||||
// level.difficultyIncrease(30) //30 is near max on hard //60 is near max on why
|
||||
// m.immuneCycle = Infinity //you can't take damage
|
||||
// spawn.setSpawnList(); //picks a couple mobs types for a themed random mob spawns
|
||||
// spawn.setSpawnList(); //picks a couple mobs types for a themed random mob spawns
|
||||
|
||||
simulation.isHorizontalFlipped = true
|
||||
if (simulation.isHorizontalFlipped) { //flip the map horizontally
|
||||
|
||||
@@ -961,7 +961,7 @@ const m = {
|
||||
}
|
||||
},
|
||||
setMaxEnergy() {
|
||||
m.maxEnergy = (tech.isMaxEnergyTech ? 0.5 : 1) + tech.bonusEnergy + tech.healMaxEnergyBonus + tech.harmonicEnergy
|
||||
m.maxEnergy = (tech.isMaxEnergyTech ? 0.5 : 1) + tech.bonusEnergy + tech.healMaxEnergyBonus + tech.harmonicEnergy + 2 * tech.isGroundState
|
||||
simulation.makeTextLog(`<span class='color-var'>m</span>.<span class='color-f'>maxEnergy</span> <span class='color-symbol'>=</span> ${(m.maxEnergy.toFixed(2))}`)
|
||||
},
|
||||
fieldMeterColor: "#0cf",
|
||||
|
||||
@@ -566,7 +566,8 @@ const powerUps = {
|
||||
}
|
||||
}
|
||||
if (options.length > 0) {
|
||||
return options[Math.floor(Math.random() * options.length)]
|
||||
// return options[Math.floor(Math.random() * options.length)]
|
||||
return options[Math.floor(Math.seededRandom(0, options.length))]
|
||||
}
|
||||
},
|
||||
choiceLog: [], //records all previous choice options
|
||||
@@ -650,7 +651,8 @@ const powerUps = {
|
||||
}
|
||||
|
||||
if (options.length > 0) {
|
||||
const choose = options[Math.floor(Math.random() * options.length)]
|
||||
// const choose = options[Math.floor(Math.random() * options.length)]
|
||||
const choose = options[Math.floor(Math.seededRandom(0, options.length))]
|
||||
const isCount = tech.tech[choose].count > 0 ? `(${tech.tech[choose].count+1}x)` : "";
|
||||
|
||||
if (tech.tech[choose].isFieldTech) {
|
||||
@@ -813,7 +815,10 @@ const powerUps = {
|
||||
}
|
||||
}
|
||||
if (options.length > 0) {
|
||||
return options[Math.floor(Math.random() * options.length)]
|
||||
// console.log(`random: ${Math.seededRandom(0, options.length)}`)
|
||||
|
||||
return options[Math.floor(Math.seededRandom(0, options.length))]
|
||||
// return options[Math.floor(Math.random() * options.length)]
|
||||
}
|
||||
},
|
||||
choiceLog: [], //records all previous choice options
|
||||
|
||||
@@ -658,7 +658,9 @@ const simulation = {
|
||||
b.setFireMethod()
|
||||
b.setFireCD();
|
||||
// simulation.updateTechHUD();
|
||||
powerUps.tech.choiceLog = []
|
||||
powerUps.tech.choiceLog = [];
|
||||
powerUps.gun.choiceLog = [];
|
||||
powerUps.field.choiceLog = [];
|
||||
powerUps.totalPowerUps = 0;
|
||||
powerUps.research.count = 0;
|
||||
m.setFillColors();
|
||||
|
||||
25
js/spawn.js
25
js/spawn.js
@@ -2,12 +2,19 @@
|
||||
const spawn = {
|
||||
nonCollideBossList: ["cellBossCulture", "bomberBoss", "powerUpBoss", "orbitalBoss", "spawnerBossCulture", "growBossCulture"],
|
||||
// other bosses: suckerBoss, laserBoss, tetherBoss, //these need a particular level to work so they are not included in the random pool
|
||||
randomLevelBoss(x, y, options = [
|
||||
"shieldingBoss", "orbitalBoss", "historyBoss", "shooterBoss", "cellBossCulture", "bomberBoss", "spiderBoss", "launcherBoss", "laserTargetingBoss",
|
||||
randomBossList: ["shieldingBoss", "orbitalBoss", "historyBoss", "shooterBoss", "cellBossCulture", "bomberBoss", "spiderBoss", "launcherBoss", "laserTargetingBoss",
|
||||
"powerUpBoss", "powerUpBossBaby", "snakeBoss", "streamBoss", "pulsarBoss", "spawnerBossCulture", "grenadierBoss", "growBossCulture", "blinkBoss",
|
||||
"snakeSpitBoss", "laserBombingBoss", "blockBoss", "revolutionBoss", "mantisBoss", "slashBoss"
|
||||
]) {
|
||||
spawn[options[Math.floor(Math.random() * options.length)]](x, y)
|
||||
],
|
||||
bossTypeSpawnOrder: [], //preset list of boss names calculated at the start of a run by the randomSeed
|
||||
bossTypeSpawnIndex: 0, //increases as the boss type cycles
|
||||
randomLevelBoss(x, y, options = []) {
|
||||
if (options.length === 0) {
|
||||
const boss = spawn.bossTypeSpawnOrder[spawn.bossTypeSpawnIndex++ % spawn.bossTypeSpawnOrder.length]
|
||||
spawn[boss](x, y)
|
||||
} else {
|
||||
spawn[options[Math.floor(Math.random() * options.length)]](x, y)
|
||||
}
|
||||
},
|
||||
pickList: ["starter", "starter"],
|
||||
fullPickList: [
|
||||
@@ -33,11 +40,17 @@ const spawn = {
|
||||
"spawner",
|
||||
"ghoster",
|
||||
],
|
||||
mobTypeSpawnOrder: [], //preset list of mob names calculated at the start of a run by the randomSeed
|
||||
mobTypeSpawnIndex: 0, //increases as the mob type cycles
|
||||
allowedGroupList: ["spinner", "striker", "springer", "laser", "focuser", "beamer", "exploder", "spawner", "shooter", "launcher", "launcherOne", "stabber", "sniper", "pulsar", "grenadier", "slasher"],
|
||||
setSpawnList() { //this is run at the start of each new level to determine the possible mobs for the level
|
||||
//each level has 2 mobs: one new mob and one from the last level
|
||||
spawn.pickList.splice(0, 1);
|
||||
spawn.pickList.push(spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)]);
|
||||
const push = spawn.mobTypeSpawnOrder[spawn.mobTypeSpawnIndex++ % spawn.mobTypeSpawnOrder.length]
|
||||
spawn.pickList.push(push);
|
||||
// if (spawn.mobTypeSpawnIndex > spawn.mobTypeSpawnOrder.length) spawn.mobTypeSpawnIndex = 0
|
||||
//each level has 2 mobs: one new mob and one from the last level
|
||||
// spawn.pickList.splice(0, 1);
|
||||
// spawn.pickList.push(spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)]);
|
||||
},
|
||||
spawnChance(chance) {
|
||||
return Math.random() < chance + 0.07 * simulation.difficulty && mob.length < -1 + 16 * Math.log10(simulation.difficulty + 1)
|
||||
|
||||
@@ -3067,7 +3067,7 @@ const tech = {
|
||||
},
|
||||
{
|
||||
name: "particle collider",
|
||||
description: `<strong>clicking</strong> <strong class='color-m'>tech</strong> while <strong>paused</strong> <strong>ejects</strong> them<br><em><strong>3%</strong> chance to convert your tech into <strong class='color-f'>energy</strong></em>`,
|
||||
description: `<strong>clicking</strong> <strong class='color-m'>tech</strong> while <strong>paused</strong> <strong>ejects</strong> them<br><em><strong>3%</strong> chance to convert that tech into <strong class='color-f'>energy</strong></em>`,
|
||||
maxCount: 1,
|
||||
count: 0,
|
||||
frequency: 1,
|
||||
|
||||
35
todo.txt
35
todo.txt
@@ -1,20 +1,33 @@
|
||||
******************************************************** NEXT PATCH **************************************************
|
||||
|
||||
tech: particle collider - in pause menu clicking a tech ejects it (5% chance to lose the power up and convert into energy)
|
||||
(also works on testing without the tech)
|
||||
all runs are now seeded
|
||||
seed defaults to the last 8 digits of UTC time in milliseconds
|
||||
set a custom randomization seed in settings
|
||||
seed controls:
|
||||
at start - boss list, mob type list, level list, horizontal flip
|
||||
during run - tech, gun, field choices, some custom level randomization
|
||||
doesn't control: mob spawns, mob size, some minor level differences, specific level boss choices, ammo reward values, specific tech effects
|
||||
|
||||
growBoss no longer goes invulnerable
|
||||
bounceBoss bullets (on reactor map)
|
||||
do 33% less damage
|
||||
move 50% slower, so they don't fill the entire map
|
||||
|
||||
ground state reworked: reduce passive energy regen by 66%, increase max energy by 200
|
||||
electronegativity: increase damage by 1% for every 9 -> 8 energy
|
||||
acetone peroxide does 300 -> 200% more self harm from explosions
|
||||
predator renamed parasitism
|
||||
bug fix with ground state
|
||||
|
||||
******************************************************** TODO ********************************************************
|
||||
|
||||
make a seed/hash system that controls only the tech/guns/fields shown
|
||||
seed will control:
|
||||
seeded random at start done - random level boss, mob types list, level order, horizontal flip
|
||||
seeded random during run - tech, gun, field choices
|
||||
not seeded random - mob spawns, mob size, minor level differences, custom level boss choices, ammo rewards, tech effect randomness
|
||||
better to only seed things at the start of the run so it doesn't mess with power up choices
|
||||
put a seed display in top right corner of splash menu, or settings?
|
||||
normal seed = full UTC time
|
||||
make option for a daily seed: seed = day+year
|
||||
give 1 extra tech for doing the daily seeded run
|
||||
make the option for the daily run, a secret exit in the intro level?
|
||||
figure out how to convert text into seed
|
||||
display seed in pause menu
|
||||
|
||||
tech upgrade to anthropic to make it trigger at 50% life and 0%
|
||||
|
||||
JUNK tech: https://bindingofisaacrebirth.fandom.com/wiki/Damocles
|
||||
|
||||
cloaking field doesn't show energy over max
|
||||
|
||||
Reference in New Issue
Block a user