Proof of concept (compute nodes to get items from names and highlight)
This commit is contained in:
parent
06f745c158
commit
0f4dba258f
7 changed files with 227 additions and 773 deletions
|
@ -1402,10 +1402,11 @@
|
||||||
<script type="text/javascript" src="../js/sq2build.js"></script>
|
<script type="text/javascript" src="../js/sq2build.js"></script>
|
||||||
<script type="text/javascript" src="../js/build_constants.js"></script>
|
<script type="text/javascript" src="../js/build_constants.js"></script>
|
||||||
<script type="text/javascript" src="../js/builder.js"></script>
|
<script type="text/javascript" src="../js/builder.js"></script>
|
||||||
|
<script type="text/javascript" src="../js/builder_graph.js"></script>
|
||||||
<script type="text/javascript" src="../js/expr_parser.js"></script>
|
<script type="text/javascript" src="../js/expr_parser.js"></script>
|
||||||
<script type="text/javascript" src="../js/items.js"></script>
|
<script type="text/javascript" src="../js/items.js"></script>
|
||||||
<script type="text/javascript" src="../js/sq2items.js"></script>
|
<script type="text/javascript" src="../js/sq2items.js"></script>
|
||||||
<script type="text/javascript" src="../js/sq2bs.js"></script>
|
<script type="text/javascript" src="../js/optimize.js"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
691
js/builder.js
691
js/builder.js
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
function getItemNameFromID(id) {
|
function getItemNameFromID(id) {
|
||||||
if (redirectMap.has(id)) {
|
if (redirectMap.has(id)) {
|
||||||
return getItemNameFromID(redirectMap.get(id));
|
return getItemNameFromID(redirectMap.get(id));
|
||||||
|
@ -51,7 +50,7 @@ function decodeBuild(url_tag) {
|
||||||
let skillpoints = [0, 0, 0, 0, 0];
|
let skillpoints = [0, 0, 0, 0, 0];
|
||||||
let level = 106;
|
let level = 106;
|
||||||
|
|
||||||
version_number = parseInt(version)
|
let version_number = parseInt(version)
|
||||||
//equipment (items)
|
//equipment (items)
|
||||||
// TODO: use filters
|
// TODO: use filters
|
||||||
if (version_number < 4) {
|
if (version_number < 4) {
|
||||||
|
@ -147,8 +146,6 @@ function decodeBuild(url_tag) {
|
||||||
for (let i in powderInputs) {
|
for (let i in powderInputs) {
|
||||||
setValue(powderInputs[i], powdering[i]);
|
setValue(powderInputs[i], powdering[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
calculateBuild(save_skp, skillpoints);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,589 +210,6 @@ function encodeBuild() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function calculateBuild(save_skp, skp){
|
|
||||||
try {
|
|
||||||
resetEditableIDs();
|
|
||||||
if (player_build) {
|
|
||||||
reset_powder_specials();
|
|
||||||
updateBoosts("skip", false);
|
|
||||||
updatePowderSpecials("skip", false);
|
|
||||||
}
|
|
||||||
let weaponName = getValue(equipmentInputs[8]);
|
|
||||||
//bruh @hpp
|
|
||||||
if (weaponName.startsWith("Morph-")) {
|
|
||||||
let equipment = [ "Morph-Stardust", "Morph-Steel", "Morph-Iron", "Morph-Gold", "Morph-Topaz", "Morph-Emerald", "Morph-Amethyst", "Morph-Ruby", weaponName.substring(6) ];
|
|
||||||
for (let i in equipment) {
|
|
||||||
setValue(equipmentInputs[i], equipment[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//updatePowderSpecials("skip"); //jank pt 1
|
|
||||||
save_skp = (typeof save_skp !== 'undefined') ? save_skp : false;
|
|
||||||
/* TODO: implement level changing
|
|
||||||
Make this entire function prettier
|
|
||||||
*/
|
|
||||||
let equipment = [ null, null, null, null, null, null, null, null, null ];
|
|
||||||
for (let i in equipment) {
|
|
||||||
let equip = getValue(equipmentInputs[i]).trim();
|
|
||||||
if (equip === "") {
|
|
||||||
equip = "No " + equipment_names[i]
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
setValue(equipmentInputs[i], equip);
|
|
||||||
}
|
|
||||||
equipment[i] = equip;
|
|
||||||
}
|
|
||||||
let powderings = [];
|
|
||||||
let errors = [];
|
|
||||||
for (const i in powderInputs) {
|
|
||||||
// read in two characters at a time.
|
|
||||||
// TODO: make this more robust.
|
|
||||||
let input = getValue(powderInputs[i]).trim();
|
|
||||||
let powdering = [];
|
|
||||||
let errorederrors = [];
|
|
||||||
while (input) {
|
|
||||||
let first = input.slice(0, 2);
|
|
||||||
let powder = powderIDs.get(first);
|
|
||||||
if (powder === undefined) {
|
|
||||||
errorederrors.push(first);
|
|
||||||
} else {
|
|
||||||
powdering.push(powder);
|
|
||||||
}
|
|
||||||
input = input.slice(2);
|
|
||||||
}
|
|
||||||
if (errorederrors.length > 0) {
|
|
||||||
if (errorederrors.length > 1)
|
|
||||||
errors.push(new IncorrectInput(errorederrors.join(""), "t6w6", powderInputs[i]));
|
|
||||||
else
|
|
||||||
errors.push(new IncorrectInput(errorederrors[0], "t6 or e3", powderInputs[i]));
|
|
||||||
}
|
|
||||||
//console.log("POWDERING: " + powdering);
|
|
||||||
powderings.push(powdering);
|
|
||||||
}
|
|
||||||
let tomes = [ null, null, null, null, null, null, null];
|
|
||||||
for (let i in tomes) {
|
|
||||||
let equip = getValue(tomeInputs[i]).trim();
|
|
||||||
if (equip === "") {
|
|
||||||
equip = "No " + tome_names[i]
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
setValue(tomeInputs[i], equip);
|
|
||||||
}
|
|
||||||
tomes[i] = equip;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
let level = document.getElementById("level-choice").value;
|
|
||||||
player_build = new Build(level, equipment, powderings, new Map(), errors, tomes);
|
|
||||||
console.log(player_build);
|
|
||||||
|
|
||||||
//isn't this deprecated?
|
|
||||||
for (let i of document.getElementsByClassName("hide-container-block")) {
|
|
||||||
i.style.display = "block";
|
|
||||||
}
|
|
||||||
for (let i of document.getElementsByClassName("hide-container-grid")) {
|
|
||||||
i.style.display = "grid";
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(player_build.toString());
|
|
||||||
displaysq2EquipOrder(document.getElementById("build-order"),player_build.equip_order);
|
|
||||||
|
|
||||||
const assigned = player_build.base_skillpoints;
|
|
||||||
const skillpoints = player_build.total_skillpoints;
|
|
||||||
for (let i in skp_order){ //big bren
|
|
||||||
setText(skp_order[i] + "-skp-base", "Original: " + skillpoints[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (save_skp) {
|
|
||||||
// TODO: reduce duplicated code, @updateStats
|
|
||||||
let skillpoints = player_build.total_skillpoints;
|
|
||||||
let delta_total = 0;
|
|
||||||
for (let i in skp_order) {
|
|
||||||
let manual_assigned = skp[i];
|
|
||||||
let delta = manual_assigned - skillpoints[i];
|
|
||||||
skillpoints[i] = manual_assigned;
|
|
||||||
player_build.base_skillpoints[i] += delta;
|
|
||||||
delta_total += delta;
|
|
||||||
}
|
|
||||||
player_build.assigned_skillpoints += delta_total;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateEditableIDs();
|
|
||||||
calculateBuildStats();
|
|
||||||
if (player_build.errored)
|
|
||||||
throw new ListError(player_build.errors);
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleBuilderError(error) {
|
|
||||||
if (error instanceof ListError) {
|
|
||||||
for (let i of error.errors) {
|
|
||||||
if (i instanceof ItemNotFound) {
|
|
||||||
i.element.textContent = i.message;
|
|
||||||
} else if (i instanceof IncorrectInput) {
|
|
||||||
if (document.getElementById(i.id) !== null) {
|
|
||||||
document.getElementById(i.id).parentElement.querySelectorAll("p.error")[0].textContent = i.message;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let msg = i.stack;
|
|
||||||
let lines = msg.split("\n");
|
|
||||||
let header = document.getElementById("header");
|
|
||||||
header.textContent = "";
|
|
||||||
for (const line of lines) {
|
|
||||||
let p = document.createElement("p");
|
|
||||||
p.classList.add("itemp");
|
|
||||||
p.textContent = line;
|
|
||||||
header.appendChild(p);
|
|
||||||
}
|
|
||||||
let p2 = document.createElement("p");
|
|
||||||
p2.textContent = "If you believe this is an error, contact hppeng on forums or discord.";
|
|
||||||
header.appendChild(p2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let msg = error.stack;
|
|
||||||
let lines = msg.split("\n");
|
|
||||||
let header = document.getElementById("header");
|
|
||||||
header.textContent = "";
|
|
||||||
for (const line of lines) {
|
|
||||||
let p = document.createElement("p");
|
|
||||||
p.classList.add("itemp");
|
|
||||||
p.textContent = line;
|
|
||||||
header.appendChild(p);
|
|
||||||
}
|
|
||||||
let p2 = document.createElement("p");
|
|
||||||
p2.textContent = "If you believe this is an error, contact hppeng on forums or discord.";
|
|
||||||
header.appendChild(p2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Updates all build statistics based on (for now) the skillpoint input fields and then calculates build stats.
|
|
||||||
*/
|
|
||||||
function updateStats() {
|
|
||||||
|
|
||||||
let specialNames = ["Quake", "Chain_Lightning", "Curse", "Courage", "Wind_Prison"];
|
|
||||||
for (const sName of specialNames) {
|
|
||||||
for (let i = 1; i < 6; i++) {
|
|
||||||
let elem = document.getElementById(sName + "-" + i);
|
|
||||||
let name = sName.replace("_", " ");
|
|
||||||
if (elem.classList.contains("toggleOn")) { //toggle the pressed button off
|
|
||||||
elem.classList.remove("toggleOn");
|
|
||||||
let special = powderSpecialStats[specialNames.indexOf(sName)];
|
|
||||||
console.log(special);
|
|
||||||
if (special["weaponSpecialEffects"].has("Damage Boost")) {
|
|
||||||
if (name === "Courage" || name === "Curse") { //courage is universal damage boost
|
|
||||||
//player_build.damageMultiplier -= special.weaponSpecialEffects.get("Damage Boost")[i-1]/100;
|
|
||||||
player_build.externalStats.set("sdPct", player_build.externalStats.get("sdPct") - special.weaponSpecialEffects.get("Damage Boost")[i-1]);
|
|
||||||
player_build.externalStats.set("mdPct", player_build.externalStats.get("mdPct") - special.weaponSpecialEffects.get("Damage Boost")[i-1]);
|
|
||||||
player_build.externalStats.set("poisonPct", player_build.externalStats.get("poisonPct") - special.weaponSpecialEffects.get("Damage Boost")[i-1]);
|
|
||||||
} else if (name === "Wind Prison") {
|
|
||||||
player_build.externalStats.set("aDamPct", player_build.externalStats.get("aDamPct") - special.weaponSpecialEffects.get("Damage Boost")[i-1]);
|
|
||||||
player_build.externalStats.get("damageBonus")[4] -= special.weaponSpecialEffects.get("Damage Boost")[i-1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let skillpoints = player_build.total_skillpoints;
|
|
||||||
let delta_total = 0;
|
|
||||||
for (let i in skp_order) {
|
|
||||||
let value = document.getElementById(skp_order[i] + "-skp").value;
|
|
||||||
if (value === ""){value = 0; setValue(skp_order[i] + "-skp", value)}
|
|
||||||
let manual_assigned = 0;
|
|
||||||
if (value.includes("+")) {
|
|
||||||
let skp = value.split("+");
|
|
||||||
for (const s of skp) {
|
|
||||||
manual_assigned += parseInt(s,10);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
manual_assigned = parseInt(value,10);
|
|
||||||
}
|
|
||||||
let delta = manual_assigned - skillpoints[i];
|
|
||||||
skillpoints[i] = manual_assigned;
|
|
||||||
player_build.base_skillpoints[i] += delta;
|
|
||||||
delta_total += delta;
|
|
||||||
}
|
|
||||||
player_build.assigned_skillpoints += delta_total;
|
|
||||||
if(player_build){
|
|
||||||
updatePowderSpecials("skip", false);
|
|
||||||
updateArmorPowderSpecials("skip", false);
|
|
||||||
updateBoosts("skip", false);
|
|
||||||
for (let id of editable_item_fields) {
|
|
||||||
player_build.statMap.set(id, parseInt(getValue(id)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
player_build.aggregateStats();
|
|
||||||
console.log(player_build.statMap);
|
|
||||||
calculateBuildStats();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Updates all IDs in the edit IDs section. Resets each input and original value text to the correct text according to the current build.
|
|
||||||
*/
|
|
||||||
function updateEditableIDs() {
|
|
||||||
if (player_build) {
|
|
||||||
for (const id of editable_item_fields) {
|
|
||||||
let edit_input = document.getElementById(id);
|
|
||||||
let val = player_build.statMap.get(id);
|
|
||||||
edit_input.value = val;
|
|
||||||
edit_input.placeholder = val;
|
|
||||||
|
|
||||||
let value_label = document.getElementById(id + "-base");
|
|
||||||
value_label.textContent = "Original Value: " + val;
|
|
||||||
//a hack to make resetting easier
|
|
||||||
value_label.value = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Resets all IDs in the edit IDs section to their "original" values.
|
|
||||||
*/
|
|
||||||
function resetEditableIDs() {
|
|
||||||
if (player_build) {
|
|
||||||
for (const id of editable_item_fields) {
|
|
||||||
let edit_input = document.getElementById(id);
|
|
||||||
let value_label = document.getElementById(id + "-base");
|
|
||||||
|
|
||||||
edit_input.value = value_label.value;
|
|
||||||
edit_input.placeholder = value_label.value;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//no player build, reset to 0
|
|
||||||
for (const id of editable_item_fields) {
|
|
||||||
let edit_input = document.getElementById(id);
|
|
||||||
|
|
||||||
edit_input.value = 0;
|
|
||||||
edit_input.placeholder = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Updates all spell boosts
|
|
||||||
*/
|
|
||||||
function updateBoosts(buttonId, recalcStats) {
|
|
||||||
let elem = document.getElementById(buttonId);
|
|
||||||
let name = buttonId.split("-")[0];
|
|
||||||
if(buttonId !== "skip") {
|
|
||||||
if (elem.classList.contains("toggleOn")) {
|
|
||||||
player_build.damageMultiplier -= damageMultipliers.get(name);
|
|
||||||
if (name === "warscream") {
|
|
||||||
player_build.defenseMultiplier -= .20;
|
|
||||||
}
|
|
||||||
if (name === "vanish") {
|
|
||||||
player_build.defenseMultiplier -= .15;
|
|
||||||
}
|
|
||||||
elem.classList.remove("toggleOn");
|
|
||||||
}else{
|
|
||||||
player_build.damageMultiplier += damageMultipliers.get(name);
|
|
||||||
if (name === "warscream") {
|
|
||||||
player_build.defenseMultiplier += .20;
|
|
||||||
}
|
|
||||||
if (name === "vanish") {
|
|
||||||
player_build.defenseMultiplier += .15;
|
|
||||||
}
|
|
||||||
elem.classList.add("toggleOn");
|
|
||||||
}
|
|
||||||
updatePowderSpecials("skip", false); //jank pt 1
|
|
||||||
} else {
|
|
||||||
for (const [key, value] of damageMultipliers) {
|
|
||||||
let elem = document.getElementById(key + "-boost")
|
|
||||||
if (elem.classList.contains("toggleOn")) {
|
|
||||||
elem.classList.remove("toggleOn");
|
|
||||||
player_build.damageMultiplier -= value;
|
|
||||||
if (key === "warscream") { player_build.defenseMultiplier -= .20 }
|
|
||||||
if (key === "vanish") { player_build.defenseMultiplier -= .15 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (recalcStats) {
|
|
||||||
calculateBuildStats();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Updates ACTIVE powder special boosts (weapons)
|
|
||||||
*/
|
|
||||||
function updatePowderSpecials(buttonId, recalcStats) {
|
|
||||||
//console.log(player_build.statMap);
|
|
||||||
|
|
||||||
let name = (buttonId).split("-")[0];
|
|
||||||
let power = (buttonId).split("-")[1]; // [1, 5]
|
|
||||||
let specialNames = ["Quake", "Chain Lightning", "Curse", "Courage", "Wind Prison"];
|
|
||||||
let powderSpecials = []; // [ [special, power], [special, power]]
|
|
||||||
|
|
||||||
|
|
||||||
if(name !== "skip"){
|
|
||||||
let elem = document.getElementById(buttonId);
|
|
||||||
if (elem.classList.contains("toggleOn")) { //toggle the pressed button off
|
|
||||||
elem.classList.remove("toggleOn");
|
|
||||||
let special = powderSpecialStats[specialNames.indexOf(name.replace("_", " "))];
|
|
||||||
if (special.weaponSpecialEffects.has("Damage Boost")) {
|
|
||||||
name = name.replace("_", " ");
|
|
||||||
if (name === "Courage" || name === "Curse") { //courage and curse are universal damage boost
|
|
||||||
player_build.externalStats.set("sdPct", player_build.externalStats.get("sdPct") - special.weaponSpecialEffects.get("Damage Boost")[power-1]);
|
|
||||||
player_build.externalStats.set("mdPct", player_build.externalStats.get("mdPct") - special.weaponSpecialEffects.get("Damage Boost")[power-1]);
|
|
||||||
player_build.externalStats.set("poisonPct", player_build.externalStats.get("poisonPct") - special.weaponSpecialEffects.get("Damage Boost")[power-1]);
|
|
||||||
//poison?
|
|
||||||
} else if (name === "Wind Prison") {
|
|
||||||
player_build.externalStats.set("aDamPct", player_build.externalStats.get("aDamPct") - special.weaponSpecialEffects.get("Damage Boost")[power-1]);
|
|
||||||
player_build.externalStats.get("damageBonus")[4] -= special.weaponSpecialEffects.get("Damage Boost")[power-1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (let i = 1;i < 6; i++) { //toggle all pressed buttons of the same powder special off
|
|
||||||
//name is same, power is i
|
|
||||||
if(document.getElementById(name.replace(" ", "_") + "-" + i).classList.contains("toggleOn")) {
|
|
||||||
document.getElementById(name.replace(" ", "_") + "-" + i).classList.remove("toggleOn");
|
|
||||||
let special = powderSpecialStats[specialNames.indexOf(name.replace("_", " "))];
|
|
||||||
if (special.weaponSpecialEffects.has("Damage Boost")) {
|
|
||||||
name = name.replace("_", " "); //might be redundant
|
|
||||||
if (name === "Courage" || name === "Curse") { //courage is universal damage boost
|
|
||||||
//player_build.damageMultiplier -= special.weaponSpecialEffects.get("Damage Boost")[i-1]/100;
|
|
||||||
player_build.externalStats.set("sdPct", player_build.externalStats.get("sdPct") - special.weaponSpecialEffects.get("Damage Boost")[i-1]);
|
|
||||||
player_build.externalStats.set("mdPct", player_build.externalStats.get("mdPct") - special.weaponSpecialEffects.get("Damage Boost")[i-1]);
|
|
||||||
player_build.externalStats.set("poisonPct", player_build.externalStats.get("poisonPct") - special.weaponSpecialEffects.get("Damage Boost")[i-1]);
|
|
||||||
} else if (name === "Wind Prison") {
|
|
||||||
player_build.externalStats.set("aDamPct", player_build.externalStats.get("aDamPct") - special.weaponSpecialEffects.get("Damage Boost")[i-1]);
|
|
||||||
player_build.externalStats.get("damageBonus")[4] -= special.weaponSpecialEffects.get("Damage Boost")[i-1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//toggle the pressed button on
|
|
||||||
elem.classList.add("toggleOn");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const sName of specialNames) {
|
|
||||||
for (let i = 1;i < 6; i++) {
|
|
||||||
if (document.getElementById(sName.replace(" ","_") + "-" + i).classList.contains("toggleOn")) {
|
|
||||||
let powderSpecial = powderSpecialStats[specialNames.indexOf(sName.replace("_"," "))];
|
|
||||||
powderSpecials.push([powderSpecial, i]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (name !== "skip") {
|
|
||||||
let elem = document.getElementById(buttonId);
|
|
||||||
if (elem.classList.contains("toggleOn")) {
|
|
||||||
let special = powderSpecialStats[specialNames.indexOf(name.replace("_", " "))];
|
|
||||||
if (special["weaponSpecialEffects"].has("Damage Boost")) {
|
|
||||||
let name = special["weaponSpecialName"];
|
|
||||||
if (name === "Courage" || name === "Curse") { //courage and curse are is universal damage boost
|
|
||||||
player_build.externalStats.set("sdPct", player_build.externalStats.get("sdPct") + special.weaponSpecialEffects.get("Damage Boost")[power-1]);
|
|
||||||
player_build.externalStats.set("mdPct", player_build.externalStats.get("mdPct") + special.weaponSpecialEffects.get("Damage Boost")[power-1]);
|
|
||||||
player_build.externalStats.set("poisonPct", player_build.externalStats.get("poisonPct") + special.weaponSpecialEffects.get("Damage Boost")[power-1]);
|
|
||||||
} else if (name === "Wind Prison") {
|
|
||||||
player_build.externalStats.set("aDamPct", player_build.externalStats.get("aDamPct") + special.weaponSpecialEffects.get("Damage Boost")[power-1]);
|
|
||||||
player_build.externalStats.get("damageBonus")[4] += special.weaponSpecialEffects.get("Damage Boost")[power-1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (recalcStats) {
|
|
||||||
calculateBuildStats();
|
|
||||||
}
|
|
||||||
displaysq2PowderSpecials(document.getElementById("powder-special-stats"), powderSpecials, player_build, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Updates PASSIVE powder special boosts (armors)
|
|
||||||
*/
|
|
||||||
function updateArmorPowderSpecials(elem_id, recalc_stats) {
|
|
||||||
//we only update the powder special + external stats if the player has a build
|
|
||||||
if (elem_id !== "skip") {
|
|
||||||
if (player_build !== undefined && player_build.weapon !== undefined && player_build.weapon.get("name") !== "No Weapon") {
|
|
||||||
let wynn_elem = elem_id.split("_")[0]; //str, dex, int, def, agi
|
|
||||||
|
|
||||||
//update the label associated w/ the slider
|
|
||||||
let elem = document.getElementById(elem_id);
|
|
||||||
let label = document.getElementById(elem_id + "_label");
|
|
||||||
let prev_label = document.getElementById(elem_id + "_prev");
|
|
||||||
|
|
||||||
let value = elem.value;
|
|
||||||
|
|
||||||
//for use in editing build stats
|
|
||||||
let prev_value = prev_label.value;
|
|
||||||
let value_diff = value - prev_value;
|
|
||||||
|
|
||||||
//update the "previous" label
|
|
||||||
prev_label.value = value;
|
|
||||||
|
|
||||||
label.textContent = label.textContent.split(":")[0] + ": " + value;
|
|
||||||
|
|
||||||
let dmg_id = elem_chars[skp_names.indexOf(wynn_elem)] + "DamPct";
|
|
||||||
let new_dmgboost = player_build.externalStats.get(dmg_id) + value_diff;
|
|
||||||
|
|
||||||
//update build external stats - the second one is the relevant one for damage calc purposes
|
|
||||||
player_build.externalStats.set(dmg_id, new_dmgboost);
|
|
||||||
player_build.externalStats.get("damageBonus")[skp_names.indexOf(wynn_elem)] = new_dmgboost;
|
|
||||||
|
|
||||||
//update the slider's graphics
|
|
||||||
let bg_color = elem_colors[skp_names.indexOf(wynn_elem)];
|
|
||||||
let pct = Math.round(100 * value / powderSpecialStats[skp_names.indexOf(wynn_elem)].cap);
|
|
||||||
elem.style.background = `linear-gradient(to right, ${bg_color}, ${bg_color} ${pct}%, #AAAAAA ${pct}%, #AAAAAA 100%)`;
|
|
||||||
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (player_build !== undefined) {
|
|
||||||
for (let i = 0; i < skp_names.length; ++i) {
|
|
||||||
skp_name = skp_names[i];
|
|
||||||
skp_char = elem_chars[i];
|
|
||||||
player_build.externalStats.set(skp_char + "DamPct", player_build.externalStats.get(skp_char + "DamPct") - document.getElementById(skp_name+"_boost_armor").value);
|
|
||||||
player_build.externalStats.get("damageBonus")[i] -= document.getElementById(skp_name+"_boost_armor").value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (recalc_stats && player_build) {
|
|
||||||
//calc build stats and display powder special
|
|
||||||
calculateBuildStats();
|
|
||||||
// displaysq2PowderSpecials(document.getElementById("powder-special-stats"), powderSpecials, player_build, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetArmorPowderSpecials() {
|
|
||||||
for (const skp of skp_names) {
|
|
||||||
document.getElementById(skp + "_boost_armor").value = 0;
|
|
||||||
document.getElementById(skp + "_boost_armor_prev").value = 0;
|
|
||||||
document.getElementById(skp + "_boost_armor").style.background = `linear-gradient(to right, #AAAAAA, #AAAAAA 0%, #AAAAAA 100%)`;
|
|
||||||
document.getElementById(skp + "_boost_armor_label").textContent = `% ${capitalizeFirst(elem_names[skp_names.indexOf(skp)])} Damage Boost: 0`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Calculates all build statistics and updates the entire display.
|
|
||||||
*/
|
|
||||||
function calculateBuildStats() {
|
|
||||||
const assigned = player_build.base_skillpoints;
|
|
||||||
const skillpoints = player_build.total_skillpoints;
|
|
||||||
let skp_effects = ["% more damage dealt.","% chance to crit.","% spell cost reduction.","% less damage taken.","% chance to dodge."];
|
|
||||||
for (let i in skp_order){ //big bren
|
|
||||||
setText(skp_order[i] + "-skp-assign", "Assign: " + assigned[i]);
|
|
||||||
setValue(skp_order[i] + "-skp", skillpoints[i]);
|
|
||||||
let linebreak = document.createElement("br");
|
|
||||||
linebreak.classList.add("itemp");
|
|
||||||
document.getElementById(skp_order[i] + "-skp-label");
|
|
||||||
setText(skp_order[i] + "-skp-pct", (skillPointsToPercentage(skillpoints[i])*100).toFixed(1).concat(skp_effects[i]));
|
|
||||||
document.getElementById(skp_order[i]+"-warnings").textContent = ''
|
|
||||||
if (assigned[i] > 100) {
|
|
||||||
let skp_warning = document.createElement("p");
|
|
||||||
skp_warning.classList.add("warning");
|
|
||||||
skp_warning.classList.add("small-text")
|
|
||||||
skp_warning.textContent += "Cannot assign " + assigned[i] + " skillpoints in " + ["Strength","Dexterity","Intelligence","Defense","Agility"][i] + " manually.";
|
|
||||||
document.getElementById(skp_order[i]+"-warnings").textContent = ''
|
|
||||||
document.getElementById(skp_order[i]+"-warnings").appendChild(skp_warning);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let summarybox = document.getElementById("summary-box");
|
|
||||||
summarybox.textContent = "";
|
|
||||||
let skpRow = document.createElement("p");
|
|
||||||
|
|
||||||
let remainingSkp = document.createElement("p");
|
|
||||||
remainingSkp.classList.add("scaled-font");
|
|
||||||
let remainingSkpTitle = document.createElement("b");
|
|
||||||
remainingSkpTitle.textContent = "Assigned " + player_build.assigned_skillpoints + " skillpoints. Remaining skillpoints: ";
|
|
||||||
let remainingSkpContent = document.createElement("b");
|
|
||||||
remainingSkpContent.textContent = "" + (levelToSkillPoints(player_build.level) - player_build.assigned_skillpoints);
|
|
||||||
remainingSkpContent.classList.add(levelToSkillPoints(player_build.level) - player_build.assigned_skillpoints < 0 ? "negative" : "positive");
|
|
||||||
|
|
||||||
remainingSkp.appendChild(remainingSkpTitle);
|
|
||||||
remainingSkp.appendChild(remainingSkpContent);
|
|
||||||
|
|
||||||
|
|
||||||
summarybox.append(skpRow);
|
|
||||||
summarybox.append(remainingSkp);
|
|
||||||
if(player_build.assigned_skillpoints > levelToSkillPoints(player_build.level)){
|
|
||||||
let skpWarning = document.createElement("span");
|
|
||||||
//skpWarning.classList.add("itemp");
|
|
||||||
skpWarning.classList.add("warning");
|
|
||||||
skpWarning.textContent = "WARNING: Too many skillpoints need to be assigned!";
|
|
||||||
let skpCount = document.createElement("p");
|
|
||||||
skpCount.classList.add("warning");
|
|
||||||
skpCount.textContent = "For level " + (player_build.level>101 ? "101+" : player_build.level) + ", there are only " + levelToSkillPoints(player_build.level) + " skill points available.";
|
|
||||||
summarybox.append(skpWarning);
|
|
||||||
summarybox.append(skpCount);
|
|
||||||
}
|
|
||||||
let lvlWarning;
|
|
||||||
for (const item of player_build.items) {
|
|
||||||
let item_lvl;
|
|
||||||
if (item.get("crafted")) {
|
|
||||||
//item_lvl = item.get("lvlLow") + "-" + item.get("lvl");
|
|
||||||
item_lvl = item.get("lvlLow");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
item_lvl = item.get("lvl");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (player_build.level < item_lvl) {
|
|
||||||
if (!lvlWarning) {
|
|
||||||
lvlWarning = document.createElement("p");
|
|
||||||
lvlWarning.classList.add("itemp");
|
|
||||||
lvlWarning.classList.add("warning");
|
|
||||||
lvlWarning.textContent = "WARNING: A level " + player_build.level + " player cannot use some piece(s) of this build."
|
|
||||||
}
|
|
||||||
let baditem = document.createElement("p");
|
|
||||||
baditem.classList.add("nocolor");
|
|
||||||
baditem.classList.add("itemp");
|
|
||||||
baditem.textContent = item.get("displayName") + " requires level " + item.get("lvl") + " to use.";
|
|
||||||
lvlWarning.appendChild(baditem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(lvlWarning){
|
|
||||||
summarybox.append(lvlWarning);
|
|
||||||
}
|
|
||||||
for (const [setName, count] of player_build.activeSetCounts) {
|
|
||||||
const bonus = sets[setName].bonuses[count-1];
|
|
||||||
// console.log(setName);
|
|
||||||
if (bonus["illegal"]) {
|
|
||||||
let setWarning = document.createElement("p");
|
|
||||||
setWarning.classList.add("itemp");
|
|
||||||
setWarning.classList.add("warning");
|
|
||||||
setWarning.textContent = "WARNING: illegal item combination: " + setName
|
|
||||||
summarybox.append(setWarning);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i in player_build.items) {
|
|
||||||
displaysq2ExpandedItem(player_build.items[i], buildFields[i]);
|
|
||||||
collapse_element("#"+equipment_keys[i]+"-tooltip");
|
|
||||||
}
|
|
||||||
|
|
||||||
displaysq2ArmorStats(player_build);
|
|
||||||
displaysq2BuildStats('overall-stats', player_build, build_all_display_commands);
|
|
||||||
displaysq2BuildStats("offensive-stats",player_build, build_offensive_display_commands);
|
|
||||||
displaysq2SetBonuses("set-info",player_build);
|
|
||||||
displaysq2WeaponStats(player_build);
|
|
||||||
|
|
||||||
let meleeStats = player_build.getMeleeStats();
|
|
||||||
displaysq2MeleeDamage(document.getElementById("build-melee-stats"), document.getElementById("build-melee-statsAvg"), meleeStats);
|
|
||||||
|
|
||||||
displaysq2DefenseStats(document.getElementById("defensive-stats"),player_build);
|
|
||||||
|
|
||||||
displaysq2PoisonDamage(document.getElementById("build-poison-stats"),player_build);
|
|
||||||
|
|
||||||
let spells = spell_table[player_build.weapon.get("type")];
|
|
||||||
for (let i = 0; i < 4; ++i) {
|
|
||||||
let parent_elem = document.getElementById("spell"+i+"-info");
|
|
||||||
let overallparent_elem = document.getElementById("spell"+i+"-infoAvg");
|
|
||||||
displaysq2SpellDamage(parent_elem, overallparent_elem, player_build, spells[i], i+1);
|
|
||||||
}
|
|
||||||
|
|
||||||
location.hash = encodeBuild();
|
|
||||||
clear_highlights();
|
|
||||||
}
|
|
||||||
|
|
||||||
function copyBuild() {
|
function copyBuild() {
|
||||||
if (player_build) {
|
if (player_build) {
|
||||||
copyTextToClipboard(url_base+location.hash);
|
copyTextToClipboard(url_base+location.hash);
|
||||||
|
@ -916,107 +330,6 @@ function toggleButton(button_id) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function optimizeStrDex() {
|
|
||||||
if (!player_build) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const remaining = levelToSkillPoints(player_build.level) - player_build.assigned_skillpoints;
|
|
||||||
const base_skillpoints = player_build.base_skillpoints;
|
|
||||||
const max_str_boost = 100 - base_skillpoints[0];
|
|
||||||
const max_dex_boost = 100 - base_skillpoints[1];
|
|
||||||
if (Math.min(remaining, max_str_boost, max_dex_boost) < 0) return; // Unwearable
|
|
||||||
|
|
||||||
const base_total_skillpoints = player_build.total_skillpoints;
|
|
||||||
let str_bonus = remaining;
|
|
||||||
let dex_bonus = 0;
|
|
||||||
let best_skillpoints = player_build.total_skillpoints;
|
|
||||||
let best_damage = 0;
|
|
||||||
for (let i = 0; i <= remaining; ++i) {
|
|
||||||
let total_skillpoints = base_total_skillpoints.slice();
|
|
||||||
total_skillpoints[0] += Math.min(max_str_boost, str_bonus);
|
|
||||||
total_skillpoints[1] += Math.min(max_dex_boost, dex_bonus);
|
|
||||||
|
|
||||||
// Calculate total 3rd spell damage
|
|
||||||
let spell = spell_table[player_build.weapon.get("type")][2];
|
|
||||||
const stats = player_build.statMap;
|
|
||||||
let critChance = skillPointsToPercentage(total_skillpoints[1]);
|
|
||||||
let save_damages = [];
|
|
||||||
let spell_parts;
|
|
||||||
if (spell.parts) {
|
|
||||||
spell_parts = spell.parts;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
spell_parts = spell.variants.DEFAULT;
|
|
||||||
for (const majorID of stats.get("activeMajorIDs")) {
|
|
||||||
if (majorID in spell.variants) {
|
|
||||||
spell_parts = spell.variants[majorID];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let total_damage = 0;
|
|
||||||
for (const part of spell_parts) {
|
|
||||||
if (part.type === "damage") {
|
|
||||||
let _results = calculateSpellDamage(stats, part.conversion,
|
|
||||||
stats.get("sdRaw"), stats.get("sdPct") + player_build.externalStats.get("sdPct"),
|
|
||||||
part.multiplier / 100, player_build.weapon, total_skillpoints,
|
|
||||||
player_build.damageMultiplier, player_build.externalStats);
|
|
||||||
let totalDamNormal = _results[0];
|
|
||||||
let totalDamCrit = _results[1];
|
|
||||||
let results = _results[2];
|
|
||||||
let tooltipinfo = _results[3];
|
|
||||||
|
|
||||||
for (let i = 0; i < 6; ++i) {
|
|
||||||
for (let j in results[i]) {
|
|
||||||
results[i][j] = results[i][j].toFixed(2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let nonCritAverage = (totalDamNormal[0]+totalDamNormal[1])/2 || 0;
|
|
||||||
let critAverage = (totalDamCrit[0]+totalDamCrit[1])/2 || 0;
|
|
||||||
let averageDamage = (1-critChance)*nonCritAverage+critChance*critAverage || 0;
|
|
||||||
|
|
||||||
save_damages.push(averageDamage);
|
|
||||||
if (part.summary == true) {
|
|
||||||
total_damage = averageDamage;
|
|
||||||
}
|
|
||||||
} else if (part.type === "total") {
|
|
||||||
total_damage = 0;
|
|
||||||
for (let i in part.factors) {
|
|
||||||
total_damage += save_damages[i] * part.factors[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // END Calculate total 3rd spell damage (total_damage)
|
|
||||||
if (total_damage > best_damage) {
|
|
||||||
best_damage = total_damage;
|
|
||||||
best_skillpoints = total_skillpoints.slice();
|
|
||||||
}
|
|
||||||
|
|
||||||
str_bonus -= 1;
|
|
||||||
dex_bonus += 1;
|
|
||||||
|
|
||||||
}
|
|
||||||
// TODO: reduce duplicated code, @calculateBuild
|
|
||||||
let skillpoints = player_build.total_skillpoints;
|
|
||||||
let delta_total = 0;
|
|
||||||
for (let i in skp_order) {
|
|
||||||
let manual_assigned = best_skillpoints[i];
|
|
||||||
let delta = manual_assigned - skillpoints[i];
|
|
||||||
skillpoints[i] = manual_assigned;
|
|
||||||
player_build.base_skillpoints[i] += delta;
|
|
||||||
delta_total += delta;
|
|
||||||
}
|
|
||||||
player_build.assigned_skillpoints += delta_total;
|
|
||||||
|
|
||||||
try {
|
|
||||||
calculateBuildStats();
|
|
||||||
if (player_build.errored)
|
|
||||||
throw new ListError(player_build.errors);
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
handleBuilderError(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Learn and use await
|
// TODO: Learn and use await
|
||||||
function init() {
|
function init() {
|
||||||
console.log("builder.js init");
|
console.log("builder.js init");
|
||||||
|
@ -1026,6 +339,7 @@ function init() {
|
||||||
update_field(i);
|
update_field(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function init2() {
|
function init2() {
|
||||||
load_ing_init(init);
|
load_ing_init(init);
|
||||||
}
|
}
|
||||||
|
@ -1033,5 +347,4 @@ function init3() {
|
||||||
load_tome_init(init2)
|
load_tome_init(init2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
load_init(init3);
|
load_init(init3);
|
||||||
|
|
21
js/builder_graph.js
Normal file
21
js/builder_graph.js
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
|
||||||
|
|
||||||
|
let item_nodes = [];
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
for (const [eq, none_item] of zip(equipment_fields, none_items)) {
|
||||||
|
let input_field = document.getElementById(eq+"-choice");
|
||||||
|
let item_image = document.getElementById(eq+"-img");
|
||||||
|
|
||||||
|
let item_input = new ItemInputNode(eq+'-input', input_field, none_item);
|
||||||
|
item_nodes.push(item_input);
|
||||||
|
new ItemInputDisplayNode(eq+'-display', input_field, item_image).link_to(item_input);
|
||||||
|
new PrintNode(eq+'-debug').link_to(item_input);
|
||||||
|
//document.querySelector("#"+eq+"-tooltip").setAttribute("onclick", "collapse_element('#"+ eq +"-tooltip');"); //toggle_plus_minus('" + eq + "-pm');
|
||||||
|
|
||||||
|
}
|
||||||
|
let weapon_image = document.getElementById("weapon-img");
|
||||||
|
new WeaponDisplayNode('weapon-type', weapon_image).link_to(item_nodes[8]);
|
||||||
|
console.log("Set up graph");
|
||||||
|
|
||||||
|
});
|
|
@ -13,6 +13,7 @@ class ComputeNode {
|
||||||
this.update_task = null;
|
this.update_task = null;
|
||||||
this.update_time = Date.now();
|
this.update_time = Date.now();
|
||||||
this.fail_cb = false; // Set to true to force updates even if parent failed.
|
this.fail_cb = false; // Set to true to force updates even if parent failed.
|
||||||
|
this.calc_inputs = new Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,19 +24,40 @@ class ComputeNode {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.update_time = timestamp;
|
this.update_time = timestamp;
|
||||||
|
this.set_value(this.compute_func(this.calc_inputs));
|
||||||
|
}
|
||||||
|
|
||||||
let value_map = Map();
|
/**
|
||||||
for (const input of this.inputs) {
|
* Set this node's value directly. Notifies children.
|
||||||
value_map.set(input.name, input.get_value());
|
*/
|
||||||
}
|
set_value(value) {
|
||||||
this.value = this.compute_func(value_map);
|
let timestamp = Date.now();
|
||||||
|
this.update_time = timestamp;
|
||||||
|
this.value = value;
|
||||||
for (const child of this.children) {
|
for (const child of this.children) {
|
||||||
if (this.value || child.fail_cb) {
|
child.set_input(this.name, this.value, timestamp);
|
||||||
child.update();
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set an input value. Propagates calculation if all inputs are present.
|
||||||
|
*/
|
||||||
|
set_input(input_name, value, timestamp) {
|
||||||
|
if (value || this.fail_cb) {
|
||||||
|
this.calc_inputs.set(input_name, value)
|
||||||
|
if (this.calc_inputs.size === this.inputs.length) {
|
||||||
|
this.update(timestamp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove cached input values to this calculation.
|
||||||
|
*/
|
||||||
|
clear_cache() {
|
||||||
|
this.calc_inputs = new Map();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get value of this compute node. Can't trigger update cascades (push based update, not pull based.)
|
* Get value of this compute node. Can't trigger update cascades (push based update, not pull based.)
|
||||||
*/
|
*/
|
||||||
|
@ -72,6 +94,19 @@ function calcSchedule(node) {
|
||||||
}, 500);
|
}, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class PrintNode extends ComputeNode {
|
||||||
|
|
||||||
|
constructor(name) {
|
||||||
|
super(name);
|
||||||
|
this.fail_cb = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
compute_func(input_map) {
|
||||||
|
console.log([this.name, input_map]);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Node for getting an item's stats from an item input field.
|
* Node for getting an item's stats from an item input field.
|
||||||
*/
|
*/
|
||||||
|
@ -85,9 +120,9 @@ class ItemInputNode extends ComputeNode {
|
||||||
*/
|
*/
|
||||||
constructor(name, item_input_field, none_item) {
|
constructor(name, item_input_field, none_item) {
|
||||||
super(name);
|
super(name);
|
||||||
this.input_field.setAttribute("input", () => calcSchedule(this));
|
|
||||||
this.input_field = item_input_field;
|
this.input_field = item_input_field;
|
||||||
this.none_item = expandItem(none_item);
|
this.input_field.addEventListener("input", () => calcSchedule(this));
|
||||||
|
this.none_item = new Item(none_item);
|
||||||
}
|
}
|
||||||
|
|
||||||
compute_func(input_map) {
|
compute_func(input_map) {
|
||||||
|
@ -107,16 +142,22 @@ class ItemInputNode extends ComputeNode {
|
||||||
item = getCraftFromHash(item_text);
|
item = getCraftFromHash(item_text);
|
||||||
}
|
}
|
||||||
else if (itemMap.has(item_text)) {
|
else if (itemMap.has(item_text)) {
|
||||||
item = Item(itemMap.get(item_text));
|
item = new Item(itemMap.get(item_text));
|
||||||
}
|
}
|
||||||
else if (tomeMap.has(item_text)) {
|
else if (tomeMap.has(item_text)) {
|
||||||
item = Item(tomeMap.get(item_text));
|
item = new Item(tomeMap.get(item_text));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!item || item.statMap.get('type') !== this.none_item.statMap.get('type')) {
|
if (item) {
|
||||||
return null;
|
let type_match;
|
||||||
|
if (this.none_item.statMap.get('category') === 'weapon') {
|
||||||
|
type_match = item.statMap.get('category') === 'weapon';
|
||||||
|
} else {
|
||||||
|
type_match = item.statMap.get('type') === this.none_item.statMap.get('type');
|
||||||
|
}
|
||||||
|
if (type_match) { return item; }
|
||||||
}
|
}
|
||||||
return item;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,6 +207,6 @@ class WeaponDisplayNode extends ComputeNode {
|
||||||
const [item] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element
|
const [item] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element
|
||||||
|
|
||||||
const type = item.statMap.get('type');
|
const type = item.statMap.get('type');
|
||||||
this.image_field.setAttribute('src', '../media/items/new/generic-'+type+'.png');
|
this.image.setAttribute('src', '../media/items/new/generic-'+type+'.png');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
86
js/load.js
86
js/load.js
|
@ -201,59 +201,61 @@ function load_init(init_func) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// List of 'raw' "none" items (No Helmet, etc), in order helmet, chestplate... ring1, ring2, brace, neck, weapon.
|
||||||
|
for (const it of itemTypes) {
|
||||||
|
itemLists.set(it, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
let none_items = [
|
||||||
|
["armor", "helmet", "No Helmet"],
|
||||||
|
["armor", "chestplate", "No Chestplate"],
|
||||||
|
["armor", "leggings", "No Leggings"],
|
||||||
|
["armor", "boots", "No Boots"],
|
||||||
|
["accessory", "ring", "No Ring 1"],
|
||||||
|
["accessory", "ring", "No Ring 2"],
|
||||||
|
["accessory", "bracelet", "No Bracelet"],
|
||||||
|
["accessory", "necklace", "No Necklace"],
|
||||||
|
["weapon", "dagger", "No Weapon"],
|
||||||
|
];
|
||||||
|
for (let i = 0; i < none_items.length; i++) {
|
||||||
|
let item = Object();
|
||||||
|
item.slots = 0;
|
||||||
|
item.category = none_items[i][0];
|
||||||
|
item.type = none_items[i][1];
|
||||||
|
item.name = none_items[i][2];
|
||||||
|
item.displayName = item.name;
|
||||||
|
item.set = null;
|
||||||
|
item.quest = null;
|
||||||
|
item.skillpoints = [0, 0, 0, 0, 0];
|
||||||
|
item.has_negstat = false;
|
||||||
|
item.reqs = [0, 0, 0, 0, 0];
|
||||||
|
item.fixID = true;
|
||||||
|
item.tier = "Normal";
|
||||||
|
item.id = 10000 + i;
|
||||||
|
item.nDam = "0-0";
|
||||||
|
item.eDam = "0-0";
|
||||||
|
item.tDam = "0-0";
|
||||||
|
item.wDam = "0-0";
|
||||||
|
item.fDam = "0-0";
|
||||||
|
item.aDam = "0-0";
|
||||||
|
clean_item(item);
|
||||||
|
|
||||||
|
none_items[i] = item;
|
||||||
|
}
|
||||||
|
|
||||||
function init_maps() {
|
function init_maps() {
|
||||||
//warp
|
//warp
|
||||||
itemMap = new Map();
|
itemMap = new Map();
|
||||||
/* Mapping from item names to set names. */
|
/* Mapping from item names to set names. */
|
||||||
idMap = new Map();
|
idMap = new Map();
|
||||||
redirectMap = new Map();
|
redirectMap = new Map();
|
||||||
for (const it of itemTypes) {
|
items = items.concat(none_items);
|
||||||
itemLists.set(it, []);
|
|
||||||
}
|
|
||||||
|
|
||||||
let noneItems = [
|
|
||||||
["armor", "helmet", "No Helmet"],
|
|
||||||
["armor", "chestplate", "No Chestplate"],
|
|
||||||
["armor", "leggings", "No Leggings"],
|
|
||||||
["armor", "boots", "No Boots"],
|
|
||||||
["accessory", "ring", "No Ring 1"],
|
|
||||||
["accessory", "ring", "No Ring 2"],
|
|
||||||
["accessory", "bracelet", "No Bracelet"],
|
|
||||||
["accessory", "necklace", "No Necklace"],
|
|
||||||
["weapon", "dagger", "No Weapon"],
|
|
||||||
];
|
|
||||||
for (let i = 0; i < noneItems.length; i++) {
|
|
||||||
let item = Object();
|
|
||||||
item.slots = 0;
|
|
||||||
item.category = noneItems[i][0];
|
|
||||||
item.type = noneItems[i][1];
|
|
||||||
item.name = noneItems[i][2];
|
|
||||||
item.displayName = item.name;
|
|
||||||
item.set = null;
|
|
||||||
item.quest = null;
|
|
||||||
item.skillpoints = [0, 0, 0, 0, 0];
|
|
||||||
item.has_negstat = false;
|
|
||||||
item.reqs = [0, 0, 0, 0, 0];
|
|
||||||
item.fixID = true;
|
|
||||||
item.tier = "Normal";
|
|
||||||
item.id = 10000 + i;
|
|
||||||
item.nDam = "0-0";
|
|
||||||
item.eDam = "0-0";
|
|
||||||
item.tDam = "0-0";
|
|
||||||
item.wDam = "0-0";
|
|
||||||
item.fDam = "0-0";
|
|
||||||
item.aDam = "0-0";
|
|
||||||
clean_item(item);
|
|
||||||
|
|
||||||
noneItems[i] = item;
|
|
||||||
}
|
|
||||||
items = items.concat(noneItems);
|
|
||||||
//console.log(items);
|
//console.log(items);
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
if (item.remapID === undefined) {
|
if (item.remapID === undefined) {
|
||||||
itemLists.get(item.type).push(item.displayName);
|
itemLists.get(item.type).push(item.displayName);
|
||||||
itemMap.set(item.displayName, item);
|
itemMap.set(item.displayName, item);
|
||||||
if (noneItems.includes(item)) {
|
if (none_items.includes(item)) {
|
||||||
idMap.set(item.id, "");
|
idMap.set(item.id, "");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
101
js/optimize.js
Normal file
101
js/optimize.js
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
function optimizeStrDex() {
|
||||||
|
if (!player_build) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const remaining = levelToSkillPoints(player_build.level) - player_build.assigned_skillpoints;
|
||||||
|
const base_skillpoints = player_build.base_skillpoints;
|
||||||
|
const max_str_boost = 100 - base_skillpoints[0];
|
||||||
|
const max_dex_boost = 100 - base_skillpoints[1];
|
||||||
|
if (Math.min(remaining, max_str_boost, max_dex_boost) < 0) return; // Unwearable
|
||||||
|
|
||||||
|
const base_total_skillpoints = player_build.total_skillpoints;
|
||||||
|
let str_bonus = remaining;
|
||||||
|
let dex_bonus = 0;
|
||||||
|
let best_skillpoints = player_build.total_skillpoints;
|
||||||
|
let best_damage = 0;
|
||||||
|
for (let i = 0; i <= remaining; ++i) {
|
||||||
|
let total_skillpoints = base_total_skillpoints.slice();
|
||||||
|
total_skillpoints[0] += Math.min(max_str_boost, str_bonus);
|
||||||
|
total_skillpoints[1] += Math.min(max_dex_boost, dex_bonus);
|
||||||
|
|
||||||
|
// Calculate total 3rd spell damage
|
||||||
|
let spell = spell_table[player_build.weapon.get("type")][2];
|
||||||
|
const stats = player_build.statMap;
|
||||||
|
let critChance = skillPointsToPercentage(total_skillpoints[1]);
|
||||||
|
let save_damages = [];
|
||||||
|
let spell_parts;
|
||||||
|
if (spell.parts) {
|
||||||
|
spell_parts = spell.parts;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
spell_parts = spell.variants.DEFAULT;
|
||||||
|
for (const majorID of stats.get("activeMajorIDs")) {
|
||||||
|
if (majorID in spell.variants) {
|
||||||
|
spell_parts = spell.variants[majorID];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let total_damage = 0;
|
||||||
|
for (const part of spell_parts) {
|
||||||
|
if (part.type === "damage") {
|
||||||
|
let _results = calculateSpellDamage(stats, part.conversion,
|
||||||
|
stats.get("sdRaw"), stats.get("sdPct") + player_build.externalStats.get("sdPct"),
|
||||||
|
part.multiplier / 100, player_build.weapon, total_skillpoints,
|
||||||
|
player_build.damageMultiplier, player_build.externalStats);
|
||||||
|
let totalDamNormal = _results[0];
|
||||||
|
let totalDamCrit = _results[1];
|
||||||
|
let results = _results[2];
|
||||||
|
let tooltipinfo = _results[3];
|
||||||
|
|
||||||
|
for (let i = 0; i < 6; ++i) {
|
||||||
|
for (let j in results[i]) {
|
||||||
|
results[i][j] = results[i][j].toFixed(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let nonCritAverage = (totalDamNormal[0]+totalDamNormal[1])/2 || 0;
|
||||||
|
let critAverage = (totalDamCrit[0]+totalDamCrit[1])/2 || 0;
|
||||||
|
let averageDamage = (1-critChance)*nonCritAverage+critChance*critAverage || 0;
|
||||||
|
|
||||||
|
save_damages.push(averageDamage);
|
||||||
|
if (part.summary == true) {
|
||||||
|
total_damage = averageDamage;
|
||||||
|
}
|
||||||
|
} else if (part.type === "total") {
|
||||||
|
total_damage = 0;
|
||||||
|
for (let i in part.factors) {
|
||||||
|
total_damage += save_damages[i] * part.factors[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // END Calculate total 3rd spell damage (total_damage)
|
||||||
|
if (total_damage > best_damage) {
|
||||||
|
best_damage = total_damage;
|
||||||
|
best_skillpoints = total_skillpoints.slice();
|
||||||
|
}
|
||||||
|
|
||||||
|
str_bonus -= 1;
|
||||||
|
dex_bonus += 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
// TODO: reduce duplicated code, @calculateBuild
|
||||||
|
let skillpoints = player_build.total_skillpoints;
|
||||||
|
let delta_total = 0;
|
||||||
|
for (let i in skp_order) {
|
||||||
|
let manual_assigned = best_skillpoints[i];
|
||||||
|
let delta = manual_assigned - skillpoints[i];
|
||||||
|
skillpoints[i] = manual_assigned;
|
||||||
|
player_build.base_skillpoints[i] += delta;
|
||||||
|
delta_total += delta;
|
||||||
|
}
|
||||||
|
player_build.assigned_skillpoints += delta_total;
|
||||||
|
|
||||||
|
try {
|
||||||
|
calculateBuildStats();
|
||||||
|
if (player_build.errored)
|
||||||
|
throw new ListError(player_build.errors);
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
handleBuilderError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
25
js/utils.js
25
js/utils.js
|
@ -3,31 +3,6 @@ const url_base = getUrl.protocol + "//" + getUrl.host + "/" + getUrl.pathname.sp
|
||||||
|
|
||||||
const zip = (a, b) => a.map((k, i) => [k, b[i]]);
|
const zip = (a, b) => a.map((k, i) => [k, b[i]]);
|
||||||
|
|
||||||
//updates all the OGP tags for a webpage. Should be called when build changes
|
|
||||||
function updateOGP() {
|
|
||||||
//update the embed URL
|
|
||||||
let url_elem = document.getElementById("ogp-url");
|
|
||||||
if (url_elem) {
|
|
||||||
url_elem.content = url_base+location.hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
//update the embed text content
|
|
||||||
let build_elem = document.getElementById("ogp-build-list");
|
|
||||||
if (build_elem && player_build) {
|
|
||||||
let text = "WynnBuilder build:\n"+
|
|
||||||
"> "+player_build.helmet.get("displayName")+"\n"+
|
|
||||||
"> "+player_build.chestplate.get("displayName")+"\n"+
|
|
||||||
"> "+player_build.leggings.get("displayName")+"\n"+
|
|
||||||
"> "+player_build.boots.get("displayName")+"\n"+
|
|
||||||
"> "+player_build.ring1.get("displayName")+"\n"+
|
|
||||||
"> "+player_build.ring2.get("displayName")+"\n"+
|
|
||||||
"> "+player_build.bracelet.get("displayName")+"\n"+
|
|
||||||
"> "+player_build.necklace.get("displayName")+"\n"+
|
|
||||||
"> "+player_build.weapon.get("displayName")+" ["+player_build.weapon.get("powders").map(x => powderNames.get(x)).join("")+"]";
|
|
||||||
build_elem.content = text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function clamp(num, low, high){
|
function clamp(num, low, high){
|
||||||
return Math.min(Math.max(num, low), high);
|
return Math.min(Math.max(num, low), high);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue