Ready for dev branch merge

This commit is contained in:
hppeng 2022-06-20 06:12:22 -07:00
parent 4d7197fc53
commit 18d21e21ae
7 changed files with 332 additions and 94 deletions

View file

@ -156,7 +156,8 @@ class Build{
}
getBaseSpellCost(spellIdx, cost) {
// old intelligence: cost = Math.ceil(cost * (1 - skillPointsToPercentage(this.total_skillpoints[2])));
// old intelligence:
cost = Math.ceil(cost * (1 - skillPointsToPercentage(this.total_skillpoints[2])));
cost += this.statMap.get("spRaw"+spellIdx);
return Math.floor(cost * (1 + this.statMap.get("spPct"+spellIdx) / 100));
}
@ -206,44 +207,6 @@ class Build{
return damages_results.concat([totalDamNorm,totalDamCrit,normDPS,critDPS,avgDPS,adjAtkSpd, singleHitTotal]).concat(results[3]);
}
/*
Get all defensive stats for this build.
*/
getDefenseStats(){
const stats = this.statMap;
let defenseStats = [];
let def_pct = skillPointsToPercentage(this.total_skillpoints[3]);
let agi_pct = skillPointsToPercentage(this.total_skillpoints[4]);
//total hp
let totalHp = stats.get("hp") + stats.get("hpBonus");
if (totalHp < 5) totalHp = 5;
defenseStats.push(totalHp);
//EHP
let ehp = [totalHp, totalHp];
let defMult = classDefenseMultipliers.get(this.weapon.statMap.get("type"));
ehp[0] /= (1-def_pct)*(1-agi_pct)*(2-defMult);
ehp[1] /= (1-def_pct)*(2-defMult);
defenseStats.push(ehp);
//HPR
let totalHpr = rawToPct(stats.get("hprRaw"), stats.get("hprPct")/100.);
defenseStats.push(totalHpr);
//EHPR
let ehpr = [totalHpr, totalHpr];
ehpr[0] /= (1-def_pct)*(1-agi_pct)*(2-defMult);
ehpr[1] /= (1-def_pct)*(2-defMult);
defenseStats.push(ehpr);
//skp stats
defenseStats.push([ def_pct*100, agi_pct*100]);
//eledefs - TODO POWDERS
let eledefs = [0, 0, 0, 0, 0];
for(const i in skp_elements){ //kinda jank but ok
eledefs[i] = rawToPct(stats.get(skp_elements[i] + "Def"), stats.get(skp_elements[i] + "DefPct")/100.);
}
defenseStats.push(eledefs);
//[total hp, [ehp w/ agi, ehp w/o agi], total hpr, [ehpr w/ agi, ehpr w/o agi], [def%, agi%], [edef,tdef,wdef,fdef,adef]]
return defenseStats;
}
/* Get all stats for this build. Stores in this.statMap.
@pre The build itself should be valid. No checking of validity of pieces is done here.

View file

@ -132,12 +132,16 @@ function decodeBuild(url_tag) {
for (let i in powder_inputs) {
setValue(powder_inputs[i], powdering[i]);
}
for (let i in skillpoints) {
console.log(skillpoints[i]);
setValue(skp_order[i] + "-skp", skillpoints[i]);
}
}
}
/* Stores the entire build in a string using B64 encoding and adds it to the URL.
*/
function encodeBuild(build, powders) {
function encodeBuild(build, powders, skillpoints) {
if (build) {
let build_string;
@ -148,7 +152,6 @@ function encodeBuild(build, powders) {
tome_string = "";
for (const item of build.items) {
if (item.statMap.get("custom")) {
let custom = "CI-"+encodeCustom(item, true);
build_string += Base64.fromIntN(custom.length, 3) + custom;
@ -167,8 +170,8 @@ function encodeBuild(build, powders) {
}
}
for (const skp of skp_order) {
build_string += Base64.fromIntN(getValue(skp + "-skp"), 2); // Maximum skillpoints: 2048
for (const skp of skillpoints) {
build_string += Base64.fromIntN(skp, 2); // Maximum skillpoints: 2048
}
build_string += Base64.fromIntN(build.level, 2);
for (const _powderset of powders) {

View file

@ -160,10 +160,17 @@ class BuildEncodeNode extends ComputeNode {
input_map.get('boots-powder'),
input_map.get('weapon-powder')
];
const skillpoints = [
input_map.get('str'),
input_map.get('dex'),
input_map.get('int'),
input_map.get('def'),
input_map.get('agi')
];
// TODO: grr global state for copy button..
player_build = build;
build_powders = powders;
return encodeBuild(build, powders);
return encodeBuild(build, powders, skillpoints);
}
}
@ -295,12 +302,50 @@ class SpellSelectNode extends ComputeNode {
}
}
/*
* Get all defensive stats for this build.
*/
function getDefenseStats(stats) {
let defenseStats = [];
let def_pct = skillPointsToPercentage(stats.get('def'));
let agi_pct = skillPointsToPercentage(stats.get('agi'));
//total hp
let totalHp = stats.get("hp") + stats.get("hpBonus");
if (totalHp < 5) totalHp = 5;
defenseStats.push(totalHp);
//EHP
let ehp = [totalHp, totalHp];
let defMult = stats.get("classDef");
ehp[0] /= (1-def_pct)*(1-agi_pct)*(2-defMult);
ehp[1] /= (1-def_pct)*(2-defMult);
defenseStats.push(ehp);
//HPR
let totalHpr = rawToPct(stats.get("hprRaw"), stats.get("hprPct")/100.);
defenseStats.push(totalHpr);
//EHPR
let ehpr = [totalHpr, totalHpr];
ehpr[0] /= (1-def_pct)*(1-agi_pct)*(2-defMult);
ehpr[1] /= (1-def_pct)*(2-defMult);
defenseStats.push(ehpr);
//skp stats
defenseStats.push([ def_pct*100, agi_pct*100]);
//eledefs - TODO POWDERS
let eledefs = [0, 0, 0, 0, 0];
for(const i in skp_elements){ //kinda jank but ok
eledefs[i] = rawToPct(stats.get(skp_elements[i] + "Def"), stats.get(skp_elements[i] + "DefPct")/100.);
}
defenseStats.push(eledefs);
//[total hp, [ehp w/ agi, ehp w/o agi], total hpr, [ehpr w/ agi, ehpr w/o agi], [def%, agi%], [edef,tdef,wdef,fdef,adef]]
return defenseStats;
}
/**
* Compute spell damage of spell parts.
* Currently kinda janky / TODO while we rework the internal rep. of spells.
*
* Signature: SpellDamageCalcNode(weapon-input: Item,
* build: Build,
* stats: StatMap,
* weapon-powder: List[powder],
* spell-info: [Spell, SpellParts]) => List[SpellDamage]
*/
@ -311,25 +356,31 @@ class SpellDamageCalcNode extends ComputeNode {
compute_func(input_map) {
const weapon = new Map(input_map.get('weapon-input').statMap);
const build = input_map.get('build');
const weapon_powder = input_map.get('weapon-powder');
const damage_mult = 1; // TODO: hook up
const spell_info = input_map.get('spell-info');
const spell_parts = spell_info[1];
const stats = input_map.get('stats');
const skillpoints = [
stats.get('str'),
stats.get('dex'),
stats.get('int'),
stats.get('def'),
stats.get('agi')
];
weapon.set("powders", weapon_powder);
let spell_results = []
let stats = build.statMap;
for (const part of spell_parts) {
if (part.type === "damage") {
let results = calculateSpellDamage(stats, part.conversion,
stats.get("sdRaw") + stats.get("rainbowRaw"), stats.get("sdPct"),
part.multiplier / 100, weapon, build.total_skillpoints, damage_mult);
part.multiplier / 100, weapon, skillpoints, damage_mult);
spell_results.push(results);
} else if (part.type === "heal") {
// TODO: wynn2 formula
let heal_amount = (part.strength * build.getDefenseStats()[0] * Math.max(0.5,Math.min(1.75, 1 + 0.5 * stats.get("wDamPct")/100))).toFixed(2);
let heal_amount = (part.strength * getDefenseStats(stats)[0] * Math.max(0.5,Math.min(1.75, 1 + 0.5 * stats.get("wDamPct")/100))).toFixed(2);
spell_results.push(heal_amount);
} else if (part.type === "total") {
// TODO: remove "total" type
@ -345,7 +396,7 @@ class SpellDamageCalcNode extends ComputeNode {
* Display spell damage from spell parts.
* Currently kinda janky / TODO while we rework the internal rep. of spells.
*
* Signature: SpellDisplayNode(build: Build,
* Signature: SpellDisplayNode(stats: StatMap,
* spell-info: [Spell, SpellParts],
* spell-damage: List[SpellDamage]) => null
*/
@ -375,29 +426,231 @@ class SpellDisplayNode extends ComputeNode {
* Signature: BuildDisplayNode(build: Build) => null
*/
class BuildDisplayNode extends ComputeNode {
constructor(spell_num) { super("builder-stats-display"); }
constructor() { super("builder-stats-display"); }
compute_func(input_map) {
if (input_map.size !== 1) { throw "BuildDisplayNode accepts exactly one input (build)"; }
const [build] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element
displayBuildStats('overall-stats', build, build_all_display_commands);
displayBuildStats("offensive-stats", build, build_offensive_display_commands);
const build = input_map.get('build');
const stats = input_map.get('stats');
displayBuildStats('overall-stats', build, build_all_display_commands, stats);
displayBuildStats("offensive-stats", build, build_offensive_display_commands, stats);
displaySetBonuses("set-info", build);
let meleeStats = build.getMeleeStats();
displayMeleeDamage(document.getElementById("build-melee-stats"), document.getElementById("build-melee-statsAvg"), meleeStats);
displayDefenseStats(document.getElementById("defensive-stats"), build);
displayDefenseStats(document.getElementById("defensive-stats"), stats);
displayPoisonDamage(document.getElementById("build-poison-stats"), build);
displayEquipOrder(document.getElementById("build-order"), build.equip_order);
}
}
/**
* Show warnings for skillpoints, level, set bonus for a build
* Also shosw skill point remaining and other misc. info
*
* Signature: DisplayBuildWarningNode(build: Build, str: int, dex: int, int: int, def: int, agi: int) => null
*/
class DisplayBuildWarningsNode extends ComputeNode {
constructor() { super("builder-show-warnings"); }
compute_func(input_map) {
const build = input_map.get('build');
const min_assigned = build.base_skillpoints;
const base_totals = build.total_skillpoints;
const skillpoints = [
input_map.get('str'),
input_map.get('dex'),
input_map.get('int'),
input_map.get('def'),
input_map.get('agi')
];
let skp_effects = ["% more damage dealt.","% chance to crit.","% spell cost reduction.","% less damage taken.","% chance to dodge."];
let total_assigned = 0;
for (let i in skp_order){ //big bren
const assigned = skillpoints[i] - base_totals[i] + min_assigned[i]
setText(skp_order[i] + "-skp-assign", "Assign: " + assigned);
setValue(skp_order[i] + "-skp", skillpoints[i]);
let linebreak = document.createElement("br");
linebreak.classList.add("itemp");
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 > 100) {
let skp_warning = document.createElement("p");
skp_warning.classList.add("warning"); skp_warning.classList.add("small-text");
skp_warning.textContent += "Cannot assign " + assigned + " skillpoints in " + ["Strength","Dexterity","Intelligence","Defense","Agility"][i] + " manually.";
document.getElementById(skp_order[i]+"-warnings").appendChild(skp_warning);
}
total_assigned += assigned;
}
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 " + total_assigned + " skillpoints. Remaining skillpoints: ";
let remainingSkpContent = document.createElement("b");
remainingSkpContent.textContent = "" + (levelToSkillPoints(build.level) - total_assigned);
remainingSkpContent.classList.add(levelToSkillPoints(build.level) - total_assigned < 0 ? "negative" : "positive");
remainingSkp.appendChild(remainingSkpTitle);
remainingSkp.appendChild(remainingSkpContent);
summarybox.append(skpRow);
summarybox.append(remainingSkp);
if(total_assigned > levelToSkillPoints(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 " + (build.level>101 ? "101+" : build.level) + ", there are only " + levelToSkillPoints(build.level) + " skill points available.";
summarybox.append(skpWarning);
summarybox.append(skpCount);
}
let lvlWarning;
for (const item of build.items) {
let item_lvl;
if (item.statMap.get("crafted")) {
//item_lvl = item.get("lvlLow") + "-" + item.get("lvl");
item_lvl = item.statMap.get("lvlLow");
}
else {
item_lvl = item.statMap.get("lvl");
}
if (build.level < item_lvl) {
if (!lvlWarning) {
lvlWarning = document.createElement("p");
lvlWarning.classList.add("itemp");
lvlWarning.classList.add("warning");
lvlWarning.textContent = "WARNING: A level " + 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_lvl + " to use.";
lvlWarning.appendChild(baditem);
}
}
if(lvlWarning){
summarybox.append(lvlWarning);
}
for (const [setName, count] of build.activeSetCounts) {
const bonus = sets.get(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);
}
}
}
}
/**
* Aggregate stats from the build and from inputs.
*
* Signature: AggregateStatsNode(build: Build, *args) => StatMap
*/
class AggregateStatsNode extends ComputeNode {
constructor() { super("builder-aggregate-stats"); }
compute_func(input_map) {
const build = input_map.get('build');
const weapon = input_map.get('weapon');
const output_stats = new Map(build.statMap);
for (const [k, v] of input_map.entries()) {
if (k === 'build') {
continue;
}
output_stats.set(k, v);
}
output_stats.set('classDef', classDefenseMultipliers.get(weapon.statMap.get("type")));
return output_stats;
}
}
/**
* Set the editble id fields.
*
* Signature: EditableIDSetterNode(build: Build) => null
*/
class EditableIDSetterNode extends ComputeNode {
constructor() { super("builder-id-setter"); }
compute_func(input_map) {
if (input_map.size !== 1) { throw "EditableIDSetterNode accepts exactly one input (build)"; }
const [build] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element
for (const id of editable_item_fields) {
document.getElementById(id).value = build.statMap.get(id);
}
}
}
/**
* Set skillpoint fields from build.
* This is separate because..... because of the way we work with edit ids vs skill points during the load sequence....
*
* Signature: SkillPointSetterNode(build: Build) => null
*/
class SkillPointSetterNode extends ComputeNode {
constructor(notify_nodes) {
super("builder-skillpoint-setter");
this.notify_nodes = notify_nodes;
}
compute_func(input_map) {
console.log("mmm");
if (input_map.size !== 1) { throw "SkillPointSetterNode accepts exactly one input (build)"; }
const [build] = input_map.values(); // Extract values, pattern match it into size one list and bind to first element
for (const [idx, elem] of skp_order.entries()) {
setText(elem + "-skp-base", "Original: " + build.base_skillpoints[idx]);
document.getElementById(elem+'-skp').value = build.total_skillpoints[idx];
}
// NOTE: DO NOT merge these loops for performance reasons!!!
for (const node of this.notify_nodes) {
node.mark_dirty();
}
for (const node of this.notify_nodes) {
node.update();
}
}
}
/**
* Get number (possibly summed) from a text input.
*
* Signature: SumNumberInputNode() => int
*/
class SumNumberInputNode extends InputNode {
compute_func(input_map) {
const value = this.input_field.value;
if (value === "") { value = 0; }
let input_num = 0;
if (value.includes("+")) {
let skp = value.split("+");
for (const s of skp) {
const val = parseInt(s,10);
if (isNaN(val)) {
return null;
}
input_num += val;
}
} else {
input_num = parseInt(value,10);
if (isNaN(input_num)) {
return null;
}
}
return input_num;
}
}
let item_nodes = [];
@ -433,7 +686,6 @@ function builder_graph_init() {
build_node.link_to(input);
}
build_node.link_to(level_input);
new BuildDisplayNode().link_to(build_node, 'build');
let build_encode_node = new BuildEncodeNode();
build_encode_node.link_to(build_node, 'build');
@ -448,48 +700,69 @@ function builder_graph_init() {
build_encode_node.link_to(powder_node, input);
}
// Edit IDs setter declared up here to set ids so they will be populated by default.
let edit_id_output = new EditableIDSetterNode();
edit_id_output.link_to(build_node);
// Phase 2/2: Set up editable IDs, skill points; use decodeBuild() skill points, calculate damage
let build_disp_node = new BuildDisplayNode()
build_disp_node.link_to(build_node, 'build');
let build_warnings_node = new DisplayBuildWarningsNode();
build_warnings_node.link_to(build_node, 'build');
// Create one node that will be the "aggregator node" (listen to all the editable id nodes, as well as the build_node (for non editable stats) and collect them into one statmap)
let stat_agg_node = new AggregateStatsNode();
stat_agg_node.link_to(build_node, 'build').link_to(item_nodes[8], 'weapon');
let edit_input_nodes = [];
for (const field of editable_item_fields) {
// Create nodes that listens to each editable id input, the node name should match the "id"
const elem = document.getElementById(field);
const node = new SumNumberInputNode('builder-'+field+'-input', elem);
stat_agg_node.link_to(node, field);
edit_input_nodes.push(node);
}
for (const skp of skp_order) {
const elem = document.getElementById(skp+'-skp');
const node = new SumNumberInputNode('builder-'+skp+'-input', elem);
stat_agg_node.link_to(node, skp);
build_encode_node.link_to(node, skp);
build_warnings_node.link_to(node, skp);
edit_input_nodes.push(node);
}
build_disp_node.link_to(stat_agg_node, 'stats');
for (const input_node of item_nodes.concat(powder_nodes)) {
input_node.update();
}
level_input.update();
// Phase 2/2: Set up editable IDs, skill points; use decodeBuild() skill points, calculate damage
// Create one node that will be the "aggregator node" (listen to all the editable id nodes, as well as the build_node (for non editable stats) and collect them into one statmap)
// let stat_agg_node =
for (const field of editable_elems) {
// Create nodes that listens to each editable id input, the node name should match the "id"
// stat_agg_node.link_to( ... )
}
// Also do something similar for skill points
for (let i = 0; i < 4; ++i) {
let spell_node = new SpellSelectNode(i);
spell_node.link_to(build_node, 'build');
// link and rewrite spell_node to the stat agg node
// spell_node.link_to(stat_agg_node, 'stats')
// TODO: link and rewrite spell_node to the stat agg node
spell_node.link_to(stat_agg_node, 'stats')
let calc_node = new SpellDamageCalcNode(i);
calc_node.link_to(item_nodes[8], 'weapon-input');
calc_node.link_to(build_node, 'build');
calc_node.link_to(powder_nodes[4], 'weapon-powder');
calc_node.link_to(spell_node, 'spell-info');
calc_node.link_to(item_nodes[8], 'weapon-input').link_to(stat_agg_node, 'stats')
.link_to(powder_nodes[4], 'weapon-powder').link_to(spell_node, 'spell-info');
spelldmg_nodes.push(calc_node);
let display_node = new SpellDisplayNode(i);
display_node.link_to(build_node, 'build');
display_node.link_to(build_node, 'build'); // TODO: same here..
display_node.link_to(spell_node, 'spell-info');
display_node.link_to(calc_node, 'spell-damage');
}
for (const node of edit_input_nodes) {
node.update();
}
// Create a node that binds the build to the edit ids text boxes
// (make it export to the textboxes)
// This node is a bit tricky since it has to make a "weak link" (directly mark dirty and call update() for each of the stat nodes).
// IMPORTANT: mark all children dirty, then update each child, in that order (2 loops). else performance issues will be bad
// let id_exporter_node = ...
// id_exporter_node.link_to(build_node, 'build')
let skp_output = new SkillPointSetterNode(edit_input_nodes);
skp_output.link_to(build_node);
// call node.update() for each skillpoint node and stat edit listener node manually
// NOTE: the text boxes for skill points are already filled out by decodeBuild() so this will fix them

View file

@ -94,6 +94,7 @@ class ComputeNode {
this.inputs_dirty_count += 1;
}
parent_node.children.push(this);
return this;
}
}

View file

@ -175,6 +175,7 @@ function getCustomFromHash(hash) {
}
}
statMap.set("hash", "CI-" + name);
statMap.set("custom", true);
return new Custom(statMap);
}
} catch (error) {

View file

@ -50,7 +50,7 @@ function displaySetBonuses(parent_id,build) {
const bonus = active_set.bonuses[count-1];
let mock_item = new Map();
mock_item.set("fixID", true);
mock_item.set("displayName", setName+" Set: "+count+"/"+sets[setName].items.length);
mock_item.set("displayName", setName+" Set: "+count+"/"+sets.get(setName).items.length);
let mock_minRolls = new Map();
let mock_maxRolls = new Map();
mock_item.set("minRolls", mock_minRolls);
@ -70,7 +70,7 @@ function displaySetBonuses(parent_id,build) {
}
}
function displayBuildStats(parent_id,build,command_group){
function displayBuildStats(parent_id,build,command_group,stats){
// Commands to "script" the creation of nice formatting.
// #commands create a new element.
// !elemental is some janky hack for elemental damage.
@ -82,8 +82,6 @@ function displayBuildStats(parent_id,build,command_group){
if (parent_div != null) {
setHTML(parent_id, "");
}
let stats = build.statMap;
let active_elem;
let elemental_format = false;
@ -96,7 +94,7 @@ function displayBuildStats(parent_id,build,command_group){
if (command.charAt(0) === "#") {
if (command === "#defense-stats") {
displayDefenseStats(parent_div, build, true);
displayDefenseStats(parent_div, stats, true);
}
}
if (command.charAt(0) === "!") {
@ -126,7 +124,7 @@ function displayBuildStats(parent_id,build,command_group){
style === "positive" ? style = "negative" : style = "positive";
}
if (id === "poison" && id_val > 0) {
id_val = Math.ceil(id_val*build.statMap.get("poisonPct")/100);
id_val = Math.ceil(id_val*stats.get("poisonPct")/100);
}
displayFixedID(parent_div, id, id_val, elemental_format, style);
if (id === "poison" && id_val > 0) {
@ -140,7 +138,7 @@ function displayBuildStats(parent_id,build,command_group){
prefix_elem.textContent = "\u279C With Strength: ";
let number_elem = document.createElement('b');
number_elem.classList.add(style);
number_elem.textContent = (id_val * (1+skillPointsToPercentage(build.total_skillpoints[0])) ).toFixed(0) + idSuffixes[id];
number_elem.textContent = (id_val * (1+skillPointsToPercentage(stats.get('str'))) ).toFixed(0) + idSuffixes[id];
value_elem.append(prefix_elem);
value_elem.append(number_elem);
row.appendChild(value_elem);
@ -156,7 +154,7 @@ function displayBuildStats(parent_id,build,command_group){
let prefix_elem = document.createElement('b');
prefix_elem.textContent = "\u279C Effective LS: ";
let defStats = build.getDefenseStats();
let defStats = getDefenseStats(stats);
let number_elem = document.createElement('b');
number_elem.classList.add(style);
number_elem.textContent = Math.round(defStats[1][0]*id_val/defStats[0]) + "/3s";
@ -1253,8 +1251,8 @@ function displayMeleeDamage(parent_elem, overallparent_elem, meleeStats) {
parent_elem.append(critStats);
}
function displayDefenseStats(parent_elem, build, insertSummary){
let defenseStats = build.getDefenseStats();
function displayDefenseStats(parent_elem, statMap, insertSummary){
let defenseStats = getDefenseStats(statMap);
insertSummary = (typeof insertSummary !== 'undefined') ? insertSummary : false;
if (!insertSummary) {
parent_elem.textContent = "";
@ -1297,7 +1295,7 @@ function displayDefenseStats(parent_elem, build, insertSummary){
statsTable.appendChild(hpRow);
}
let defMult = build.statMap.get("defMult");
let defMult = statMap.get("defMult");
if (!defMult) {defMult = 1}
//EHP
@ -1409,8 +1407,8 @@ function displayDefenseStats(parent_elem, build, insertSummary){
boost.classList.add("col");
boost.classList.add("text-end");
let defRaw = build.statMap.get("defRaw")[i];
let defPct = build.statMap.get("defBonus")[i]/100;
let defRaw = statMap.get("defRaw")[i];
let defPct = statMap.get("defBonus")[i]/100;
if (defRaw < 0) {
defPct >= 0 ? defPct = "- " + defPct: defPct = "+ " + defPct;
} else {

View file

@ -38,7 +38,6 @@ function calculate_skillpoints(equipment, weapon) {
setCount = 0;
activeSetCounts.set(setName, 1);
}
console.log(sets);
const new_bonus = sets.get(setName).bonuses[setCount];
//let skp_order = ["str","dex","int","def","agi"];
for (const i in skp_order) {