training mode choice for new players

electronegativity: +26->23% damage

gun cancel button doesn't show up on the initial level if you haven't done the training yet and you didn't choose a gun
after exiting initial level players are prompted to start the training levels
  only if they haven't done the training and they didn't pick up a gun, and a few other conditions
community training levels only are added when community maps setting is on and player clicks on training

if you die after clearing fewer than 4 levels the difficulty settings automatically opens
improved text formatting on updates
This commit is contained in:
landgreen
2024-01-29 21:29:39 -08:00
parent 4e6acdd5d0
commit 778a2c93b6
8 changed files with 123 additions and 88 deletions

View File

@@ -47,7 +47,7 @@
</svg>
<div id='info'>
<div id="settings">
<details>
<details id = 'settings-details'>
<summary>settings</summary>
<div style="line-height: 150%;" class="details-div">
<label for="difficulty-select" title="effects: number of mobs, damage done by mobs, damage done to mobs, mob speed, heal effects">combat difficulty:</label>
@@ -75,7 +75,7 @@
<br>
<br>
<input type="checkbox" id="community-maps" name="community-maps" style="width:17px; height:17px;">
<label for="community-maps" title="add about 12 player made levels the random n-gon level pool">community maps</label>
<label for="community-maps" title="add about 18 player made levels to the random n-gon level pool">community maps</label>
<!-- style="font-size: 1.5em; color: #058;" -->
<!-- <br>
@@ -222,7 +222,7 @@
<div>
<details id="updates">
<summary>updates</summary>
<div id="updates-div" class="details-div" style="font-size: 65%;height: 400px;overflow: scroll;max-width: 450px;"></div>
<div id="updates-div" class="details-div" style="font-size: 70%;height: 400px;overflow: scroll;max-width: 800px;"></div>
</details>
</div>
<div>

View File

@@ -4028,7 +4028,7 @@ const b = {
bullet[me] = Bodies.polygon(where.x, where.y, 12, radius, b.fireAttributes(dir, false));
Composite.add(engine.world, bullet[me]); //add bullet to world
Matter.Body.setVelocity(bullet[me], velocity);
bullet[me].calcDensity = () => 0.0007 + 0.00055 * tech.isSuperHarm + 0.0004 * tech.isBulletTeleport
bullet[me].calcDensity = function () { 0.0007 + 0.00055 * tech.isSuperHarm + 0.0004 * tech.isBulletTeleport }
Matter.Body.setDensity(bullet[me], bullet[me].calcDensity());
bullet[me].endCycle = simulation.cycle + Math.floor(270 + 90 * Math.random());
bullet[me].minDmgSpeed = 0;

View File

@@ -1752,18 +1752,19 @@ document.getElementById("updates").addEventListener("toggle", function () {
xhr.open("GET", path, true);
xhr.send();
}
let text = `<strong>n-gon</strong>: <a href="https://github.com/landgreen/n-gon/blob/master/todo.txt">todo list</a> and complete <a href="https://github.com/landgreen/n-gon/commits/master">change-log</a><hr>`
let text = `<pre><strong>n-gon</strong>: <a href="https://github.com/landgreen/n-gon/blob/master/todo.txt">todo list</a> and complete <a href="https://github.com/landgreen/n-gon/commits/master">change-log</a><hr>`
document.getElementById("updates-div").innerHTML = text
/// https://api.github.com/repos/landgreen/n-gon/stats/commit_activity
loadJSON('https://api.github.com/repos/landgreen/n-gon/commits',
function (data) {
// console.log(data)
// console.log(data[0].sha) //unique code for most recent commit
for (let i = 0, len = 20; i < len; i++) {
text += "<strong>" + data[i].commit.author.date.substr(0, 10) + "</strong> - "; //+ "<br>"
text += data[i].commit.message
if (i < len - 1) text += "<hr>"
}
text += "</pre>"
document.getElementById("updates-div").innerHTML = text.replace(/\n/g, "<br />")
},
function (xhr) {

View File

@@ -12,7 +12,7 @@ const level = {
//see level.populateLevels: (initial, ... , reservoir or factory, reactor, ... , subway, final) added later
playableLevels: ["labs", "rooftops", "skyscrapers", "warehouse", "highrise", "office", "aerie", "satellite", "sewers", "testChamber", "pavilion", "lock"],
communityLevels: ["gauntlet", "stronghold", "basement", "crossfire", "vats", "run", "ngon", "house", "perplex", "coliseum", "tunnel", "islands", "temple", "dripp", "biohazard", "stereoMadness", "yingYang", "staircase", "fortress", "commandeer", "clock", "buttonbutton", "downpour", "superNgonBros", "underpass", "cantilever", "tlinat", "ruins", "ace", "crimsonTowers", "LaunchSite", "shipwreck", "unchartedCave", "dojo"],
trainingLevels: ["walk", "crouch", "jump", "hold", "throw", "throwAt", "deflect", "heal", "fire", "nailGun", "shotGun", "superBall", "matterWave", "missile", "stack", "mine", "grenades", "harpoon", "diamagnetism"],
trainingLevels: ["walk", "crouch", "jump", "hold", "throw", "throwAt", "deflect", "heal", "fire", "nailGun", "shotGun", "superBall", "matterWave", "missile", "stack", "mine", "grenades", "harpoon"],
levels: [],
start() {
if (level.levelsCleared === 0) { //this code only runs on the first level
@@ -63,7 +63,6 @@ const level = {
// tech.tech[322].frequency = 100
// spawn.tetherBoss(1900, -500, { x: 1900, y: -500 })
// for (let i = 0; i < 40; ++i) tech.giveTech()
level[simulation.isTraining ? "walk" : "initial"]() //normal starting level **************************************************
// spawn.bodyRect(2425, -120, 200, 200);
@@ -76,6 +75,7 @@ const level = {
// for (let i = 0; i < 20; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "ammo");
// for (let i = 0; i < 2; i++) powerUps.spawn(player.position.x + Math.random() * 50, player.position.y - Math.random() * 50, "field", false);
//lore testing
// localSettings.isTrainingNotAttempted = true
// simulation.isCheating = false //true;
// for (let i = 0; i < 5; i++) tech.giveTech("undefined")
// lore.techCount = 2
@@ -252,8 +252,9 @@ const level = {
if (!level.disableExit) {
level.levelsCleared++;
level.onLevel++; //cycles map to next level
if (simulation.isTraining) {
if (level.onLevel > level.levels.length - 1) {
if (level.onLevel > level.levels.length - 1) { //if all training levels are completed
level.disableExit = true
document.getElementById("health").style.display = "none"
document.getElementById("health-bg").style.display = "none"
@@ -286,16 +287,12 @@ const level = {
tech.isDeathAvoidedThisLevel = false;
simulation.updateTechHUD();
simulation.clearNow = true; //triggers in simulation.clearMap to remove all physics bodies and setup for new map
//pop up new level info screen for a few seconds
if (!simulation.isChoosing && m.alive && (level.levels[level.onLevel] === "final" || level.levels[level.onLevel] === "reactor")) { //level.levels[level.onLevel] === "subway" ||
//pause
if (!simulation.paused) {
simulation.paused = true;
simulation.isChoosing = true; //stops p from un pausing on key down
// build.pauseGrid()
// document.body.style.cursor = "auto";
}
//build level info
document.getElementById("choose-grid").style.gridTemplateColumns = "250px"
@@ -370,68 +367,20 @@ const level = {
}
requestAnimationFrame(newLevelDraw);
// // clear
// requestAnimationFrame(() => {
// simulation.wipe();
// });
// //wireframe
// setTimeout(() => {
// requestAnimationFrame(() => {
// simulation.wipe();
// simulation.camera();
// simulation.draw.wireFrame();
// ctx.restore();
// });
// }, 500);
// //almost normal draw
// setTimeout(() => {
// requestAnimationFrame(() => {
// simulation.wipe();
// simulation.camera();
// // ctx.fillStyle = "rgba(0,0,0,0.66)"
// // ctx.fill(simulation.draw.mapPath);
// simulation.draw.drawMapPath();
// ctx.restore();
// });
// }, 1000);
// //normal draw
// setTimeout(() => {
// requestAnimationFrame(() => {
// simulation.wipe();
// simulation.camera();
// // level.custom();
// simulation.draw.cons();
// simulation.draw.body();
// // m.draw();
// // m.hold();
// level.customTopLayer();
// simulation.draw.drawMapPath();
// ctx.restore();
// });
// }, 1500);
// //unpause
// setTimeout(() => {
// document.body.style.cursor = "none";
// if (m.immuneCycle < m.cycle + 15) m.immuneCycle = m.cycle + 15; //player is immune to damage for 30 cycles
// if (simulation.paused) requestAnimationFrame(cycle);
// if (m.alive) simulation.paused = false;
// simulation.isChoosing = false; //stops p from un pausing on key down
// build.unPauseGrid()
// document.getElementById("choose-grid").style.opacity = "0"
// // document.getElementById("choose-grid").style.visibility = "hidden"
// setTimeout(() => {
// document.getElementById("choose-grid").style.visibility = "hidden"
// }, 1000);
// }, 2000);
}
}
},
unPause() {
if (m.immuneCycle < m.cycle + 15) m.immuneCycle = m.cycle + 30; //player is immune to damage for 30 cycles
if (simulation.paused) requestAnimationFrame(cycle);
if (m.alive) simulation.paused = false;
simulation.isChoosing = false; //stops p from un pausing on key down
build.unPauseGrid()
document.getElementById("choose-grid").style.opacity = "0"
setTimeout(() => {
document.getElementById("choose-grid").style.visibility = "hidden"
}, 1000);
},
populateLevels() { //run a second time if URL is loaded
if (document.getElementById("banned").value) { //remove levels from ban list in settings
const banList = document.getElementById("banned").value.replace(/,/g, ' ').replace(/\s\s+/g, ' ').replace(/[^\w\s]/g, '') //replace commas with spaces, replace double spaces with single, remove strange symbols
@@ -468,6 +417,7 @@ const level = {
if (simulation.isTraining) {
simulation.isHorizontalFlipped = false
level.levels = level.trainingLevels.slice(0) //copy array, not by just by assignment
if (simulation.isCommunityMaps) level.trainingLevels.push("diamagnetism")
} else { //add remove and shuffle levels for the normal game (not training levels)
level.levels = level.playableLevels.slice(0) //copy array, not by just by assignment
if (simulation.isCommunityMaps) {
@@ -608,7 +558,73 @@ const level = {
if (level.exitCount > 100) {
level.exitCount = 0
//prompt an option to do the training levels or continue to the normal game
if (!simulation.isChoosing && m.alive && !simulation.isTraining && !simulation.isCheating && b.inventory.length === 0 && level.levelsCleared === 0 && localSettings.isTrainingNotAttempted) {
//pause
if (!simulation.paused) {
simulation.paused = true;
simulation.isChoosing = true; //stops p from un pausing on key down
document.body.style.cursor = "auto";
document.getElementById("choose-grid").style.pointerEvents = "auto";
document.getElementById("choose-grid").style.transitionDuration = "0s";
}
//build level info
document.getElementById("choose-grid").classList.add('choose-grid-no-images');
document.getElementById("choose-grid").classList.remove('choose-grid');
document.getElementById("choose-grid").style.gridTemplateColumns = "350px"
let text = `
<div class="choose-grid-module" id = "choose-training" style = "font-size: 1em; padding:10px;color:#333;">
<h2 style="text-align: center;letter-spacing: 5px;">training</h2>
Begin the <strong>guided tutorial</strong> that shows you how to use your <strong class='color-f'>field</strong> and <strong class='color-g'>gun</strong>.
</div>
<div class="choose-grid-module" id = "choose-unPause" style = "font-size: 1em; padding:10px;color:#333;">
<h2 style="text-align: center; letter-spacing: 7px;">play</h2>
Begin the <strong>standard game</strong> where you progress through <strong>13</strong> random levels and beat the final boss.
</div>`
//use you use your <strong class='color-g'>gun</strong>, <strong class='color-f'>field</strong>, and <strong class='color-m'>tech</strong>
document.getElementById("choose-grid").innerHTML = text
//show level info
document.getElementById("choose-grid").style.opacity = "1"
document.getElementById("choose-grid").style.transitionDuration = "0.25s"; //how long is the fade in on
document.getElementById("choose-grid").style.visibility = "visible"
document.getElementById("choose-training").addEventListener("click", () => {
level.unPause()
simulation.isTraining = true
level.levelsCleared--;
level.onLevel--
simulation.isHorizontalFlipped = false
level.levels = level.trainingLevels.slice(0) //copy array, not by just by assignment
level.nextLevel()
//reset hide image style
if (localSettings.isHideImages) {
document.getElementById("choose-grid").classList.add('choose-grid-no-images');
document.getElementById("choose-grid").classList.remove('choose-grid');
} else {
document.getElementById("choose-grid").classList.add('choose-grid');
document.getElementById("choose-grid").classList.remove('choose-grid-no-images');
}
});
document.getElementById("choose-unPause").addEventListener("click", () => {
level.unPause()
level.nextLevel()
//reset hide image style
if (localSettings.isHideImages) {
document.getElementById("choose-grid").classList.add('choose-grid-no-images');
document.getElementById("choose-grid").classList.remove('choose-grid');
} else {
document.getElementById("choose-grid").classList.add('choose-grid');
document.getElementById("choose-grid").classList.remove('choose-grid-no-images');
}
});
requestAnimationFrame(() => {
ctx.fillStyle = `rgba(150,150,150,0.9)`; //`rgba(221,221,221,0.6)`;
ctx.fillRect(0, 0, canvas.width, canvas.height);
});
} else { //advance to next level
level.nextLevel()
}
}
}
},

View File

@@ -477,6 +477,8 @@ const m = {
Composite.clear(engine.world);
Engine.clear(engine);
simulation.splashReturn();
//if you die after clearing fewer than 4 levels the difficulty settings automatically opens
if (level.levelsCleared < 4 && !simulation.isTraining && !simulation.isCheating) document.getElementById("settings-details").open = true;
}, 5000);
}
},
@@ -5042,7 +5044,7 @@ const m = {
{
name: "grappling hook",
// description: `use <strong class='color-f'>energy</strong> to pull yourself towards the <strong>map</strong><br>generate <strong>6</strong> <strong class='color-f'>energy</strong> per second`,
description: `use <strong class='color-f'>energy</strong> to fire a hook that <strong>pulls</strong> player<br><strong class='color-d'>damages</strong> mobs and destroys <strong class='color-block'>blocks</strong><br>generate <strong>6</strong> <strong class='color-f'>energy</strong> per second`,
description: `use <strong class='color-f'>energy</strong> to fire a hook that <strong>pulls</strong> player<br><strong class='color-d'>damages</strong> mobs and grabs <strong class='color-block'>blocks</strong><br>generate <strong>6</strong> <strong class='color-f'>energy</strong> per second`,
effect: () => {
m.fieldFire = true;
// m.holdingMassScale = 0.01; //can hold heavier blocks with lower cost to jumping

View File

@@ -683,6 +683,8 @@ const powerUps = {
return `<div></div>`
} else if (tech.isCancelTech) {
return `<div class='cancel-card' onclick='powerUps.endDraft("${type}",true)' style="width: 115px;">randomize</div>`
} else if (level.levelsCleared === 0 && localSettings.isTrainingNotAttempted) { //don't show cancel if on initial level and haven't done tutorial
return `<div class='cancel-card' style="visibility: hidden;"></div>`
} else {
return `<div class='cancel-card' onclick='powerUps.endDraft("${type}",true)' style="width: 85px;">cancel</div>`
}
@@ -739,10 +741,11 @@ const powerUps = {
text += `<span class='cancel-card' style="width: 95px;float: right;background-color: #aaa;color:#888;">cancel</span>`
} else if (tech.isCancelTech) {
text += `<span class='cancel-card' onclick='powerUps.endDraft("${type}",true)' style="width: 115px;float: right;font-size:0.9em;padding-top:5px">randomize</span>`
} else if (level.levelsCleared === 0 && localSettings.isTrainingNotAttempted) {
text += `<span class='cancel-card' style="visibility: hidden;">cancel</span>` //don't show cancel if on initial level and haven't done tutorial
} else {
text += `<span class='cancel-card' onclick='powerUps.endDraft("${type}",true)' style="width: 95px;float: right;">cancel</span>`
}
return text + "</div>"
},
buildColumns(totalChoices, type) {

View File

@@ -250,7 +250,7 @@ const tech = {
if (tech.isBotDamage) dmg *= 1 + 0.06 * b.totalBots()
if (tech.restDamage > 1 && player.speed < 1) dmg *= tech.restDamage
if (tech.isLowEnergyDamage) dmg *= 1 + 0.7 * Math.max(0, 1 - m.energy)
if (tech.energyDamage) dmg *= 1 + m.energy * 0.26 * tech.energyDamage;
if (tech.energyDamage) dmg *= 1 + m.energy * 0.23 * tech.energyDamage;
if (tech.isDamageFromBulletCount) dmg *= 1 + bullet.length * 0.007
if (tech.isNoFireDamage && m.cycle > m.fireCDcycle + 120) dmg *= 2.11
if (tech.isSpeedDamage) dmg *= 1 + Math.min(0.88, player.speed * 0.0193)
@@ -2517,7 +2517,7 @@ const tech = {
},
{
name: "MACHO",
description: "a massive but compact object slowly follows you<br>if you are inside the <strong>MACHO</strong> <strong>+60%</strong> <strong class='color-defense'>defense</strong>",
description: "a massive compact halo object follows you<br><strong>+60%</strong> <strong class='color-defense'>defense</strong> while you are inside the <strong>MACHO</strong>",
maxCount: 1,
count: 0,
frequency: 1,
@@ -7675,7 +7675,7 @@ const tech = {
{
name: "electronegativity",
descriptionFunction() {
return `<strong>+0.26%</strong> <strong class='color-d'>damage</strong> per current stored <strong class='color-f'>energy</strong><br><em>(+${(27 * m.maxEnergy).toFixed(0)}% damage at max energy)</em>`
return `<strong>+0.23%</strong> <strong class='color-d'>damage</strong> per current stored <strong class='color-f'>energy</strong><br><em>(+${(27 * m.maxEnergy).toFixed(0)}% damage at max energy)</em>`
},
// description: "<strong>+1%</strong> <strong class='color-d'>damage</strong> per <strong>8</strong> stored <strong class='color-f'>energy</strong>",
isFieldTech: true,

View File

@@ -1,10 +1,31 @@
******************************************************** NEXT PATCH **************************************************
electronegativity: +26->23% damage
gun cancel button doesn't show up on the initial level if you haven't done the training yet and you didn't choose a gun
after exiting initial level players are prompted to start the training levels
only if they haven't done the training and they didn't pick up a gun, and a few other conditions
community training levels only are added when community maps setting is on and player clicks on training
if you die after clearing fewer than 4 levels the difficulty settings automatically opens
improved text formatting on updates
*********************************************************** TODO *****************************************************
improve new player experience
training is too long to be a tutorial
before nail gun level offer player option to continue or switch to normal game
Cosmological natural selection: do something with black holes
spawn black hole mobs
after bosses die?
after any mob dies
at the start of new levels
make new black hole bullet mobs
write code the checks version and compares it to the version from patch notes
and link to main page if they don't match
(this is because there are several outdated versions hosted on sites with ads)
foam tech: increase size of foam and increase duration, but drop speed down, so they come to a stop and just hang
allow them to harm player?
@@ -17,10 +38,6 @@ Foam slow down mobs twice as much and slowly pulls them towards the ground
https://en.m.wikipedia.org/wiki/Tungsten_hexafluoride
make a flutter variant
just move the wings to the back?
slow flap, and acceleration that increases when the flap occurs, like swimming
make grappling hook of different shapes
shapes
longer
@@ -32,12 +49,6 @@ make grappling hook of different shapes
player got stuck inside block that wasn't pick up able
grappling hook field
check for places that the player could get into but not out of
field tech ideas
Buoyancy - aerostat, but for defense: +70% defense while off the ground
too similar to degenerate matter
tech - killing a mob heals for the last damage you took
disable cloaking heal? maybe you don't need to disable, just don't heal twice
heal for 50%?
@@ -1025,6 +1036,7 @@ look up sci-fi science ideas here https://projectrho.com/public_html/rocket/
possible names for tech
sidereal - with respect to the stars (an extra rotation for time keeping)
holonomy - parallel transport of a vector leads to movement (applies to curved space)
holographic - 2-D surface can predict the 3-D space behind it? I think
hypergolic - A hypergolic propellant combination used in a rocket engine is one whose components spontaneously ignite when they come into contact with each other.
swarm intelligence - for a drone tech
genetic algorithm
@@ -1073,6 +1085,7 @@ possible names for tech
Unruh effect - accelerating makes heat/thermal particles
configuration space - holds the position of everything
******************************************************** CARS IMAGES ********************************************************
process: discord midjourney prompts -> "pixelmator pro" adjust color, repair, scale to 384x256, export PNG -> webP? -> place in /img folder