wip armor optimizer

This commit is contained in:
Frederik Palmø 2022-03-20 16:57:42 +01:00
parent 07b7321ec9
commit 054dfbe1c4
9 changed files with 344 additions and 61 deletions

230
src/armor.html Normal file
View File

@ -0,0 +1,230 @@
<!DOCTYPE html>
<html>
<head>
<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">
<title>Erdtree - Class Optimizer</title>
<meta name="description" content="">
<script src="/script/armor.js"></script>
</head>
<body onload="init();">
<nav>
<h1><a href="/index.html">Elden Ring Build Planner</a></h1>
<ul>
<li><a href="/planner.html">Build Planner</a></li>
</ul>
</nav>
<header>
<h1>Armor Optimizer</h1>
</header>
<main>
<div class="app">
<!-- settings -->
<article style="flex-basis: 30%">
<ul>
<li><b>Settings</b></li>
<hr>
<li>
<label for="equip-load"><b>Max. Equip Load</b></label>
<input style="max-width: 50px" class="stat" id="equip-load" type="number" onchange="update()"
min=0 step=0.1 value="30">
</li>
<hr>
<li>
<b>Breakpoints</b>
</li>
<li>
<div>
<input type="radio" id="fast-roll" onclick="update()" name="burden">
<label for="fast-roll">Fast Roll (up to 30% equip load)</label>
</div>
</li>
<li>
<div>
<input type="radio" id="normal-roll" onclick="update()" name="burden" checked>
<label for="normal-roll">Normal Roll (up to 70% equip load)</label>
</div>
</li>
<li>
<div>
<input type="radio" id="fast-roll" onclick="update()" name="burden">
<label for="fat-roll">Fat Roll (up to 100% equip load)</label>
</div>
</li>
<hr>
<li>
<b>Sorting</b>
</li>
<li>
<div>
<input type="radio" id="greatest-physical" name="sorting-order" onclick="update()" checked>
<label for="avg-physical">Greatest Physical Negation</label>
</div>
</li>
<li>
<div>
<input type="radio" id="greatest-elemental" name="sorting-order" onclick="update()">
<label for="avg-elemental">Greatest Elemental Negation</label>
</div>
</li>
<li>
<div>
<input type="radio" id="greatest-immunities" name="sorting-order" onclick="update()">
<label for="avg-immunities">Greatest Immunities</label>
</div>
</li>
<li>
<div>
<input type="radio" id="greatest-average" name="sorting-order" onclick="update()">
<label for="greatest-average">Greatest Weighted Average Defense</label>
</div>
</li>
<hr>
<li>
<b>Extras</b>
</li>
<li>
<div>
<input type="checkbox" id="winged-crystal-tear" onchange="update()">
<label for="winged-crystal-tear">Winged Crystal Tear (in mixed physick)</label>
</div>
</li>
<li>
<div>
<input type="checkbox" id="fashion" onchange="update()">
<label for="fashion">Fashion Mode</label>
</div>
</li>
</ul>
</article>
<!-- results -->
<article style="flex-basis: 60%; min-width: 300px">
<ul>
<li><b>Results</b></li>
<hr>
<template id="sort-result">
<table>
<thead>
<tr>
<th>Items</th>
<th>Stats</th>
</tr>
</thead>
<tbody>
<tr>
<td>Helmet</td>
<td>Stats</td>
</tr>
<tr>
<td>Chestpiece</td>
<td>Stats</td>
</tr>
<tr>
<td>Gaunlets</td>
<td>Stats</td>
</tr>
<tr>
<td>Leggings</td>
<td>Stats</td>
</tr>
</tbody>
</table>
</template>
</ul>
</article>
<!-- filter -->
<article>
<ul>
<li><b>Filter</b></li>
<hr>
<li>
<b>Locked Equipment</b>
<button id="clear-equipment" onclick="update()">Clear Locked Equipment</button>
</li>
<li>
<table>
<thead>
<tr>
<th>Headgear</th>
<th>Chestpiece</th>
<th>Gauntlets</th>
<th>Leggings</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<select type="text" id="select-head" name="locked-equipment"
onchange="update()">
<option>None</option>
</select>
</td>
<td>
<select type="text" id="select-chest" name="locked-equipment"
onchange="update()">
<option>None</option>
</select>
</td>
<td>
<select type=" text" id="select-hands" name="locked-equipment"
onchange="update()">
<option>None</option>
</select>
</td>
<td>
<select type="text" id="select-legs" name="locked-equipment"
onchange="update()">
<option>None</option>
</select>
</td>
</tr>
</tbody>
</table>
</li>
<hr>
<li>
<b>Allowed Armor</b>
</li>
</ul>
</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,5 +1,5 @@
{ {
"helms": [ "helmets": [
{ {
"id": "albinauric-mask", "id": "albinauric-mask",
"name": "Albinauric Mask", "name": "Albinauric Mask",

14
src/data/sets.json Normal file
View File

@ -0,0 +1,14 @@
{
"sets": [
{
"id": "alberichs-set",
"name": "Alberich's Set",
"pieces": [
"alberichs-pointed-hat",
"alberichs-robe",
"alberichs-bracers",
"alberichs-trousers"
]
}
]
}

View File

@ -17,7 +17,6 @@
<h1><a href="/index.html">Elden Ring Build Planner</a></h1> <h1><a href="/index.html">Elden Ring Build Planner</a></h1>
<ul> <ul>
<li><a href="/planner.html">Build Planner</a></li> <li><a href="/planner.html">Build Planner</a></li>
<li><a href="/optimizer.html">Class Optimizer</a></li>
</ul> </ul>
</nav> </nav>
@ -29,7 +28,7 @@
<div class="cards"> <div class="cards">
<article> <article>
<a href="/planner.html"> <a href="/planner.html">
<h3>Build Planner</h3> <h3>Build Planner (todo)</h3>
<br> <br>
<p>Create a new build.</p> <p>Create a new build.</p>
</a> </a>
@ -43,9 +42,17 @@
</a> </a>
</article> </article>
<article>
<a href="/armor.html">
<h3>Armor Optimizer (in progress)</h3>
<br>
<p>Maximize defensive stats.</p>
</a>
</article>
<article> <article>
<a href="/attack.html"> <a href="/attack.html">
<h3>Attack Rating Calculator</h3> <h3>Attack Rating Calculator (todo)</h3>
<br> <br>
<p>Calculate attack rating for a weapon.</p> <p>Calculate attack rating for a weapon.</p>
</a> </a>
@ -53,23 +60,15 @@
<article> <article>
<a href="/censor.html"> <a href="/censor.html">
<h3>Censor Checker</h3> <h3>Censor Checker (todo)</h3>
<br> <br>
<p>Detect censored names.</p> <p>Detect censored names.</p>
</a> </a>
</article> </article>
<article>
<a href="/armor.html">
<h3>Armor Optimizer</h3>
<br>
<p>Maximize defensive stats.</p>
</a>
</article>
<article> <article>
<a href="/finder.html"> <a href="/finder.html">
<h3>Weapon Finder</h3> <h3>Weapon Finder (todo)</h3>
<br> <br>
<p>Get suggestions for usable weapons.</p> <p>Get suggestions for usable weapons.</p>
</a> </a>

View File

@ -19,7 +19,6 @@
<h1><a href="/index.html">Elden Ring Build Planner</a></h1> <h1><a href="/index.html">Elden Ring Build Planner</a></h1>
<ul> <ul>
<li><a href="/planner.html">Build Planner</a></li> <li><a href="/planner.html">Build Planner</a></li>
<li><a href="/optimizer.html">Class Optimizer</a></li>
</ul> </ul>
</nav> </nav>
@ -203,8 +202,6 @@
<hr> <hr>
<template id="talisman"> <template id="talisman">
<li> <li>
<div> <div>
@ -221,17 +218,17 @@
<article> <article>
<ul> <ul>
<li> <li>
<b>Helms</b> <b>Helmets</b>
</li> </li>
<hr> <hr>
<details id="helms"> <details id="helmets">
<summary>Click to show helms</summary> <summary>Click to show helmets</summary>
<li> <li>
<div> <div>
<input type="radio" name="equipment" id="helmNone" onclick="update()" checked> <input type="radio" name="equipment" id="helm-none" onclick="update()" checked>
<label for="">None</label> <label for="">None</label>
</div> </div>
<aside></aside> <aside></aside>
@ -255,10 +252,10 @@
<table> <table>
<thead> <thead>
<tr> <tr>
<td>Skill</td> <th>Skill</th>
<td>Stat</td> <th>Stat</th>
<td>Softcaps</td> <th>Softcaps</th>
<td>Notes</td> <th>Notes</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>

View File

@ -17,7 +17,6 @@
<h1><a href="/index.html">Elden Ring Build Planner</a></h1> <h1><a href="/index.html">Elden Ring Build Planner</a></h1>
<ul> <ul>
<li><a href="/planner.html">Build Planner</a></li> <li><a href="/planner.html">Build Planner</a></li>
<li><a href="/optimizer.html">Class Optimizer</a></li>
</ul> </ul>
</nav> </nav>

View File

@ -0,0 +1,39 @@
const HELMETS = fetch("/data/armor.json")
.then(response => response.json())
.then(data => data.helmets)
.catch(error => console.log(error));
const CHESTPIECES = fetch("/data/armor.json")
.then(response => response.json())
.then(data => data.chestpieces)
.catch(error => console.log(error));
const GAUNTLETS = fetch("/data/armor.json")
.then(response => response.json())
.then(data => data.gauntlets)
.catch(error => console.log(error));
const LEGGINGS = fetch("/data/armor.json")
.then(response => response.json())
.then(data => data.leggings)
.catch(error => console.log(error));
async function init() {
}
async function update() {
let sorted = sortedCandidates();
}
function sortedCandidates() {
// determine how to sort
// get most likely candidates
let candidates = findCandidates();
// sort candidates
return candidates;
}
function findCandidates() {
return [];
}

View File

@ -6,9 +6,9 @@ const TALISMANS = fetch("/data/talismans.json")
.then(response => response.json()) .then(response => response.json())
.then(data => data.talismans) .then(data => data.talismans)
.catch(error => console.log(error)); .catch(error => console.log(error));
const HELMS = fetch("data/armor.json") const HELMETS = fetch("/data/helmets.json")
.then(response => response.json()) .then(response => response.json())
.then(data => data.helms) .then(data => data.helmets)
.catch(error => console.log(error)); .catch(error => console.log(error));
const STAT_SHORT_NAMES = [ const STAT_SHORT_NAMES = [
@ -23,17 +23,17 @@ const STAT_SHORT_NAMES = [
] ]
async function init() { async function init() {
// load and show helms // populate helmet list
let helms = await HELMS; let helmets = await HELMETS;
helms = helms.filter(helm => helm.stats != null && helm.stats != undefined); helmets = helmets.filter(helmet => helmet.stats != null && helmet.stats != undefined);
let helmTemplate = document.getElementById("helm"); let helmetTemplate = document.getElementById("helm");
let helmList = document.getElementById("helms"); let helmetList = document.getElementById("helmets");
helms.forEach(helm => { helmets.forEach(helmet => {
cloneTemplate(helmTemplate, helmList, helm); cloneTemplate(helmetTemplate, helmetList, helmet);
}); });
// load and show talismans // populate talisman list
let talismans = await TALISMANS; let talismans = await TALISMANS;
talismans = talismans.filter(talisman => talisman.stats != null && talisman.stats != undefined); talismans = talismans.filter(talisman => talisman.stats != null && talisman.stats != undefined);
@ -60,7 +60,7 @@ async function update() {
let best = sorted[0]; let best = sorted[0];
// get added stats from items // get added stats from items
let items = itemStats((await TALISMANS).concat(await HELMS)); let items = itemStats((await TALISMANS).concat(await HELMETS));
// update document // update document
document.getElementsByName("option").forEach((elem, i) => { document.getElementsByName("option").forEach((elem, i) => {
@ -91,12 +91,6 @@ async function update() {
} }
} }
function statDelta(classStats, desiredStats) {
return classStats
.map((e, i) => e < desiredStats[i] ? desiredStats[i] - e : 0)
.reduce((total, n) => total + n);
}
function sortClasses(classes, desiredStats) { function sortClasses(classes, desiredStats) {
let deltas = classes.map(c => { let deltas = classes.map(c => {
c.total = c.level + statDelta(c.stats, desiredStats); c.total = c.level + statDelta(c.stats, desiredStats);
@ -106,6 +100,12 @@ function sortClasses(classes, desiredStats) {
return deltas; return deltas;
} }
function statDelta(classStats, desiredStats) {
return classStats
.map((e, i) => e < desiredStats[i] ? desiredStats[i] - e : 0)
.reduce((total, n) => total + n);
}
function itemStats(relevantItems) { function itemStats(relevantItems) {
let ids = [...document.getElementsByName("equipment")] let ids = [...document.getElementsByName("equipment")]
.filter(elem => elem.checked) .filter(elem => elem.checked)
@ -118,7 +118,7 @@ function itemStats(relevantItems) {
function clearAll() { function clearAll() {
document.getElementsByName("desired-stat").forEach(elem => elem.value = null); document.getElementsByName("desired-stat").forEach(elem => elem.value = null);
[...document.getElementsByName("equipment")].forEach(elem => elem.checked = false); [...document.getElementsByName("equipment")].forEach(elem => elem.checked = false);
document.getElementById("helmNone").checked = true; document.getElementById("helm-none").checked = true;
update(); update();
} }

View File

@ -175,21 +175,17 @@ table {
margin-bottom: 10px; margin-bottom: 10px;
border: 1px solid var(--border); border: 1px solid var(--border);
background-color: var(--secondary);
background-color: var(--primary);
} }
td, td,
th { th {
padding: 0.5em; padding: 0.5em;
border-bottom: 1px solid var(--primary);
} }
thead { th {
font-weight: bold; border-bottom: 1px solid var(--border);
}
tbody:first-child {
font-weight: bold;
} }
/* buttons & input */ /* buttons & input */
@ -212,6 +208,7 @@ button {
text-align: center; text-align: center;
flex-grow: 1; flex-grow: 1;
flex-basis: 25%;
min-width: 200px; min-width: 200px;
border: 1px solid var(--outline); border: 1px solid var(--outline);
@ -273,19 +270,23 @@ button {
flex-grow: 50%; flex-grow: 50%;
} }
.app .stat { .app input {
text-align: center; text-align: center;
-webkit-appearance: none; border: 1px solid var(--border);
-moz-appearance: textfield;
margin-left: 10px;
max-width: 40px;
align-self: flex-end;
} }
.app input:is([disabled]) { .app input:is([disabled]) {
text-align: center;
background-color: var(--primary); background-color: var(--primary);
border: 1px solid var(--border); }
.app .stat {
align-self: flex-end;
max-width: 40px;
margin-left: 10px;
-webkit-appearance: none;
-moz-appearance: textfield;
} }
.app .stat:not([disabled]) { .app .stat:not([disabled]) {
@ -296,3 +297,7 @@ button {
margin-top: 2px; margin-top: 2px;
margin-bottom: 4px; margin-bottom: 4px;
} }
.app select {
width: 100%;
}