added buffable information

This commit is contained in:
Frederik Palmø 2022-04-07 17:06:54 +02:00
parent 117a71018e
commit 8cc402f5fd
7 changed files with 6498 additions and 3869 deletions

View File

@ -1,6 +1,6 @@
{
"no-chestpiece": {
"id": "no-chestpieces",
"id": "no-chestpiece",
"name": "No chestpiece",
"defenses": [0, 0, 0, 0, 0, 0, 0, 0],
"resistances": [0, 0, 0, 0],

View File

@ -1,5 +1,5 @@
{
"no-talisman": { "id": "no-talismans", "name": "No talisman" },
"no-talisman": { "id": "no-talisman", "name": "No talisman" },
"crimson-amber-medallion": {
"id": "crimson-amber-medallion",
"name": "Crimson Amber Medallion",

File diff suppressed because it is too large Load Diff

View File

@ -1,120 +1,56 @@
<!DOCTYPE html>
<html>
<head>
<!-- metadata -->
<meta charset="utf-8" />
<meta name="language" content="english" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<head>
<!-- metadata -->
<meta charset="utf-8">
<meta name="language" content="english">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="style/main.css" />
<link rel="stylesheet" href="style/main.css">
<!-- title and description -->
<title>Erdtree - Build Planner</title>
<meta name="description" content="" />
<meta property="og:title" content="Erdtree Build Planner" />
<meta
property="og:description"
content="Erdtree - Elden Ring build planner, armor optimizer, weapon finder and more!"
/>
<meta property="og:url" content="https://erdtree.palmoe.dk" />
<!-- title and description -->
<title>Erdtree - Build Planner</title>
<meta name="description" content="">
<meta property="og:title" content="Erdtree Build Planner">
<meta property="og:description"
content="Erdtree - Elden Ring build planner, armor optimizer, weapon finder and more!">
<meta property="og:url" content="https://erdtree.palmoe.dk">
<!-- scripts -->
<script src="script/planner.js"></script>
</head>
<!-- scripts -->
<script src="script/planner.js"></script>
</head>
<body onload="init()">
<nav>
<h1><a href="/">Elden Ring Build Planner</a></h1>
<ul>
<li><a href="/planner.html">Build Planner</a></li>
</ul>
</nav>
<body onload="init()">
<nav>
<h1><a href="/">Elden Ring Build Planner</a></h1>
<ul>
<li><a href="/planner.html">Build Planner</a></li>
</ul>
</nav>
<header>
<h1>Build Planner</h1>
</header>
<header>
<h1>Build Planner</h1>
</header>
<main>
<div class="app">
<!-- Class -->
<article style="flex-basis: 20%; max-width: 320px">
<div>
<b>Character</b>
</div>
<main>
<div class="app">
<!-- equipment -->
<article>
<ul>
<li><b>Equipment</b></li>
<hr />
<div>
<b>Name</b>
<input id="name" />
</div>
<hr>
<li>
<b>Weapons</b>
<a href="weapons.html">Optimize</a>
</li>
<li>
<div class="select">
<select id="left-hand-1" name="weapons" onchange="update()"></select>
<select id="left-hand-2" name="weapons" onchange="update()"></select>
<select id="left-hand-3" name="weapons" onchange="update()"></select>
</div>
<div class="select">
<select id="right-hand-1" name="weapons" onchange="update()"></select>
<select id="right-hand-2" name="weapons" onchange="update()"></select>
<select id="right-hand-3" name="weapons" onchange="update()"></select>
</div>
</li>
<hr>
<li>
<b>Armor</b>
<a href="armor.html">Optimize</a>
</li>
<li class="select">
<select id="helmet" name="helmets" onchange="update()"></select>
<select id="chestpiece" name="chestpieces" onchange="update()"></select>
<select id="gauntlets" name="gauntlets" onchange="update()"></select>
<select id="leggings" name="leggings" onchange="update()"></select>
</li>
<hr>
<li><b>Talismans</b></li>
<li class="select">
<select id="talisman-1" name="talismans" onchange="update()"></select>
<select id="talisman-2" name="talismans" onchange="update()"></select>
<select id="talisman-3" name="talismans" onchange="update()"></select>
<select id="talisman-4" name="talismans" onchange="update()"></select>
</li>
<hr>
<li><b>Spells</b></li>
<li>
<details>
<summary>Click to show slots</summary>
</details>
</li>
<hr>
<li>
<div></div>
<button onclick="reset('equipment')">Reset</button>
</li>
</ul>
</article>
<!-- stats -->
<article>
<ul>
<li>
<b>Statistics</b>
<a href="optimizer.html">Optimize Class</a> <!-- TODO -->
</li>
<hr>
<li>
<div>
<label for="class"><b>Starting Class</b></label>
<div>
<select id="class" onchange="update()">
<option id="wretch" selected>Wretch</option>
<option id="vagabond">Vagabond</option>
@ -128,66 +64,201 @@
<option id="confessor">Confessor</option>
</select>
</div>
</li>
<li>
<label for="level"><b>Level</b></label>
<input type="number" id="level" class="stat" disabled>
</li>
<li><br></li>
<li>
</div>
<div>
<label><b>Level</b></label>
<span>
<input type="number" id="final-level" disabled />
</span>
</div>
<div>
<label for="vigor">Vigor</label>
<input type="number" id="vigor" class="stat" name="stat">
</li>
<li>
<span>
<input type="number" id="vigor" name="final" min="0" max="99" onchange="update()" />
</span>
</div>
<div>
<label for="mind">Mind</label>
<input type="number" id="mind" class="stat" name="stat">
</li>
<li>
<span>
<input type="number" id="mind" name="final" min="0" max="99" onchange="update()" />
</span>
</div>
<div>
<label for="endurance">Endurance</label>
<input type="number" id="endurance" class="stat" name="stat">
</li>
<li>
<span>
<input type="number" id="endurance" name="final" min="0" max="99" onchange="update()" />
</span>
</div>
<div>
<label for="strength">Strength</label>
<input type="number" id="strength" class="stat" name="stat">
</li>
<li>
<span>
<input type="number" id="strength" name="final" min="0" max="99" onchange="update()" />
</span>
</div>
<div>
<label for="dexterity">Dexterity</label>
<input type="number" id="dexterity" class="stat" name="stat">
</li>
<li>
<span>
<input type="number" id="dexterity" name="final" min="0" max="99" onchange="update()" />
</span>
</div>
<div>
<label for="intelligence">Intelligence</label>
<input type="number" id="intelligence" class="stat" name="stat">
</li>
<li>
<span>
<input type="number" id="intelligence" name="final" min="0" max="99" onchange="update()" />
</span>
</div>
<div>
<label for="faith">Faith</label>
<input type="number" id="faith" class="stat" name="stat">
</li>
<li>
<span>
<input type="number" id="faith" name="final" min="0" max="99" onchange="update()" />
</span>
</div>
<div>
<label for="arcane">Arcane</label>
<input type="number" id="arcane" class="stat" name="stat">
</li>
<li>
<div></div>
<button onclick="reset('statistics')">Reset</button>
</li>
</ul>
</article>
<span>
<input type="number" id="arcane" name="final" min="0" max="99" onchange="update()" />
</span>
</div>
<hr />
<b>Import</b>
<div>
<input type="file" />
</div>
<!-- misc. -->
<article>
<ul></ul>
</article>
</div>
</main>
<hr />
<b>Export</b>
<div>
<button>Export</button>
</div>
</article>
<footer>
<h5>Erdtree Planner (<a href="https://git.palmoe.dk/vodofrede/erdtree">available under BSD-3-Clause license</a>)
</h5>
<h5>Copyright 2022 vodofrede</h5>
</footer>
</body>
<!-- equipment -->
<article style="flex-basis: 20%; max-width: 450px">
<b>Equipment</b>
<hr />
<b>Weapons & Shields</b>
<template id="picker">
<div class="popup">
<input type="image" src="/resource/icon/standard.jpg" height="20px" />
</div>
</template>
<div>
<div>
<span>
<select id="lh1" name="weapon" onchange="update()"></select>
</span>
<span>
<img id="lh1-img" src="/resource/icon/standard.jpg" width="20px" />
<select id="lh1-infusion" name="infusion"></select>
</span>
</div>
<div>
<span>
<select id="rh1" name="weapon" onchange="update()"></select>
</span>
<span>
<select id="rh1-infusion" name="infusion"></select>
<img id="rh1-img" src="/resource/icon/standard.jpg" width="20px" />
</span>
</div>
</div>
<div>
<div>
<span>
<select id="lh2" name="weapon" onchange="update()"></select>
</span>
<span>
<img id="lh2-img" src="/resource/icon/standard.jpg" width="20px" />
<select id="lh2-infusion" name="infusion"></select>
</span>
</div>
<div>
<span>
<select id="rh2" name="weapon" onchange="update()"></select>
</span>
<span>
<select id="rh2-infusion" name="infusion"></select>
<img id="rh2-img" src="/resource/icon/standard.jpg" width="20px" />
</span>
</div>
</div>
<div>
<div>
<span>
<select id="lh3" name="weapon" onchange="update()"></select>
</span>
<span>
<img id="lh3-img" src="/resource/icon/standard.jpg" width="20px" />
<select id="lh3-infusion" name="infusion"></select>
</span>
</div>
<div>
<span>
<select id="rh3" name="weapon" onchange="update()"></select>
</span>
<span>
<select id="rh3-infusion" name="infusion"></select>
<img id="rh3-img" src="/resource/icon/standard.jpg" width="20px" />
</span>
</div>
</div>
<hr />
<b>Armor</b>
<div>
<label>Helmet</label>
<select id="helmet" name="armor" onchange="update()"></select>
</div>
<div>
<label>Chestpiece</label>
<select id="chestpiece" name="armor" onchange="update()"></select>
</div>
<div>
<label>Gauntlets</label>
<select id="gauntlets" name="armor" onchange="update()"></select>
</div>
<div>
<label>Leggings</label>
<select id="leggings" name="armor" onchange="update()"></select>
</div>
<hr />
<li><b>Talismans</b></li>
<div>
<label>Talisman #1</label>
<select id="talisman-1" name="talisman" onchange="update()"></select>
</div>
<div>
<label>Talisman #2</label>
<select id="talisman-2" name="talisman" onchange="update()"></select>
</div>
<div>
<label>Talisman #3</label>
<select id="talisman-3" name="talisman" onchange="update()"></select>
</div>
<div>
<label>Talisman #4</label>
<select id="talisman-4" name="talisman" onchange="update()"></select>
</div>
<hr />
<li><b>Spells</b></li>
</article>
</div>
</main>
<footer>
<h5>
Erdtree Planner (<a href="https://git.palmoe.dk/vodofrede/erdtree"
>available under BSD-3-Clause license</a
>)
</h5>
<h5>Copyright 2022 vodofrede</h5>
</footer>
</body>
</html>

View File

@ -1,96 +1,130 @@
const HELMETS = fetch("/data/armor/helmets.json")
.then(response => response.json())
.catch(error => console.log(error));
const CHESTPIECES = fetch("/data/armor/chestpieces.json")
.then(response => response.json())
.catch(error => console.log(error));
const GAUNTLETS = fetch("/data/armor/gauntlets.json")
.then(response => response.json())
.catch(error => console.log(error));
const LEGGINGS = fetch("/data/armor/leggings.json")
.then(response => response.json())
.catch(error => console.log(error));
const WEAPONS = fetch("/data/weapons.json")
.then(response => response.json())
.catch(error => console.log(error));
const TALISMANS = fetch("/data/talismans.json")
.then(response => response.json())
.catch(error => console.log(error));
let HELMETS;
let CHESTPIECES;
let GAUNTLETS;
let LEGGINGS;
let WEAPONS;
let TALISMANS;
let EQUIPMENT;
let CLASSES;
let INFUSIONS;
const CLASSES = fetch("/data/classes.json")
.then(response => response.json())
.catch(error => console.log(error));
let startingClass = "wretch";
let popupActive = false;
let startingClass;
let populate = (select, items) => items.forEach(item => select.options.add(new Option(item.name, item.id)));
let selected = select => select.options[select.selectedIndex];
async function init() {
await populate();
await update();
}
HELMETS = await fetch("/data/helmets.json").then(response => response.json());
CHESTPIECES = await fetch("/data/chestpieces.json").then(response => response.json());
GAUNTLETS = await fetch("/data/gauntlets.json").then(response => response.json());
LEGGINGS = await fetch("/data/leggings.json").then(response => response.json());
TALISMANS = await fetch("/data/talismans.json").then(response => response.json());
WEAPONS = await fetch("/data/weapons.json").then(response => response.json());
EQUIPMENT = { ...HELMETS, ...CHESTPIECES, ...GAUNTLETS, ...LEGGINGS, ...TALISMANS, ...WEAPONS };
async function update() {
// get current chosen starting class
let classSelect = document.getElementById("class");
let currentClass = await (CLASSES.then(classes => {
return classes.find(c => c.id == classSelect.options[classSelect.selectedIndex].id);
}));
CLASSES = await fetch("/data/classes.json").then(response => response.json());
INFUSIONS = await fetch("/data/infusions.json").then(response => response.json());
// get virtual stats
let items = await itemStats();
console.log(items);
// update statistics screen
let prevClass = startingClass ?? currentClass;
[...document.getElementsByName("stat")].forEach((stat, i) => {
stat.value = ((stat.value == "" || (stat.value - parseInt(items[i])) == prevClass.stats[i]) ? currentClass.stats[i] + parseInt(items[i]) : parseInt(stat.value) + parseInt(items[i]));
// populate selects
[...document.getElementsByName("weapon")].forEach(select => {
populate(select, Object.values(WEAPONS));
});
populate(document.getElementById("helmet"), Object.values(HELMETS));
populate(document.getElementById("chestpiece"), Object.values(CHESTPIECES));
populate(document.getElementById("gauntlets"), Object.values(GAUNTLETS));
populate(document.getElementById("leggings"), Object.values(LEGGINGS));
[...document.getElementsByName("talisman")].forEach(select => {
populate(select, Object.values(TALISMANS));
});
// override ctrl/cmd+s
document.addEventListener("keydown", event => {
if (!(event.isComposing || event.code === "229")) {
if ((event.ctrlKey || event.metaKey) && event.key == "s") {
event.preventDefault();
exportBuild();
}
}
});
document.getElementById("level").value = [...document.getElementsByName("stat")]
.reduce((total, stat) => total + parseInt(stat.value), 0) - 79;
startingClass = currentClass;
}
function reset(area) {
switch (area) {
case "equipment":
let types = ["helmets", "chestpieces", "gauntlets", "leggings", "weapons", "talismans"];
types.forEach(ty => {
let selects = [...document.getElementsByName(ty)];
selects.forEach(select => {
select.selectedIndex = 0;
})
})
break;
case "statistics":
document.getElementById("class").selectedIndex = 0;
[...document.getElementsByName("stat")].forEach(stat => stat.value = 10);
break;
default:
break;
}
update();
}
async function itemStats() {
let helmet = document.getElementById("helmet");
let talismans = document.getElementsByName("talismans");
let ids = [helmet, ...talismans]
.map(elem => elem.options[elem.selectedIndex].value)
.filter(id => !id.startsWith("no-"));
let relevant = [await HELMETS, await TALISMANS].flat().filter(item => ids.includes(item.id));
function update() {
// get current chosen starting class
let classSelect = document.getElementById("class");
let currentClass = CLASSES.find(c => c.id == classSelect.options[classSelect.selectedIndex].id);
return relevant.reduce((total, item) => total.map((stat, i) => stat += item.stats[i]), [0, 0, 0, 0, 0, 0, 0, 0]);
// item stats
let itemStats = [...document.getElementsByName("armor"), ...document.getElementsByName("talisman")].reduce(
(stats, select) => {
let item = EQUIPMENT[select.options[select.selectedIndex].value];
return stats.map((stat, i) => (item.stats != undefined ? stat + item.stats[i] : stat));
},
[0, 0, 0, 0, 0, 0, 0, 0],
);
if (currentClass != startingClass) {
// reset stats if other class was chosen
[...document.getElementsByName("final")].forEach((el, i) => {
el.value = Math.min(currentClass.stats[i] + itemStats[i], 99);
});
startingClass = currentClass;
} else {
// clamp stat ranges
[...document.getElementsByName("final")].forEach((el, i) => {
el.value = Math.min(Math.max(currentClass.stats[i] + itemStats[i], el.value), 99);
});
}
// show only available infusions for each weapon
let weapons = [...document.getElementsByName("weapon")]
.map(sl => sl.options[sl.selectedIndex].value)
.map(id => WEAPONS[id]);
let infusions = [...document.getElementsByName("infusion")];
infusions
.map((inf, i) => [weapons[i], inf])
.forEach(([weapon, infusionSelect]) => {
infusionSelect.length = 0;
Object.keys(weapon.infusions).forEach(infusionId => {
let infusion = INFUSIONS[infusionId];
infusionSelect.options.add(new Option(infusion.name, infusion.id));
});
});
// update infusion icons
[...document.getElementsByName("infusion")].forEach(select => {});
// calculate total level
// document.getElementById("final-level").value = currentClass.level + additional.reduce((sum, n) => sum + n);
}
async function populate() {
Promise.all([HELMETS, CHESTPIECES, GAUNTLETS, LEGGINGS, WEAPONS, TALISMANS]).then(itemTypes => {
itemTypes.forEach((itemType, i) => {
let ty = ["helmets", "chestpieces", "gauntlets", "leggings", "weapons", "talismans"][i];
let selects = [...document.getElementsByName(ty)];
selects.forEach(select => {
itemType.forEach(item => {
select.options.add(new Option(item.name, item.id));
})
})
});
});
}
function importBuild(buildString) {}
function exportBuild() {
let classSelect = document.getElementById("class");
let itemStats = [...document.getElementsByName("armor"), ...document.getElementsByName("talisman")].reduce(
(stats, select) => {
let item = EQUIPMENT[selected(select).value];
return stats.map((stat, i) => (item.stats != undefined ? stat + item.stats[i] : stat));
},
[0, 0, 0, 0, 0, 0, 0, 0],
);
console.log(itemStats);
let build = {
name: document.getElementById("name").value,
class: classSelect.options[classSelect.selectedIndex].value,
stats: [...document.getElementsByName("final")].map((stat, i) => stat.value - itemStats[i]),
weapons: [...document.getElementsByName("weapon")].map(selected).map(w => w.value),
infusions: [...document.getElementsByName("infusion")].map(selected).map(i => i.value),
armor: [...document.getElementsByName("armor")].map(selected).map(a => a.value),
talismans: [...document.getElementsByName("talisman")].map(selected).map(t => t.value),
};
let file = new Blob([JSON.stringify(build, null, 4)], { type: "text/plain" });
let link = document.createElement("a");
link.href = URL.createObjectURL(file);
link.download = "build-" + (build.name || build.class.toLowerCase()) + ".json";
link.click();
}

View File

@ -51,8 +51,8 @@ html {
*::before,
*::after {
box-sizing: inherit;
margin: 0;
padding: 0;
margin: 0px;
padding: 0px;
}
body > * {
@ -98,6 +98,12 @@ hr {
margin-bottom: 0.3rem;
}
span {
display: flex;
justify-content: center;
gap: 0.2rem;
}
/* nav */
nav {
/* position */
@ -215,16 +221,19 @@ label {
}
input {
width: auto;
margin: 0rem 0rem 0rem 0rem;
color: black;
}
select {
width: 100%;
margin: 0rem 0rem 0.2rem 0rem;
color: black;
}
select * {
select option {
overflow: hidden;
color: black;
}
@ -258,6 +267,7 @@ select * {
.app {
display: flex;
flex-flow: row wrap;
justify-content: center;
gap: 30px;
}

File diff suppressed because it is too large Load Diff