Add files via upload

This commit is contained in:
reschan 2021-01-10 16:01:59 -05:00 committed by GitHub
parent ad61401ee5
commit 96f4c69c8d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 21157 additions and 292 deletions

10206
1_20_ci.json Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,3 +1,8 @@
const baseDamageMultiplier = [ 0.51, 0.83, 1.5, 2.05, 2.5, 3.1, 4.3 ];
const attackSpeeds = ["SUPER_SLOW", "VERY_SLOW", "SLOW", "NORMAL", "FAST", "VERY_FAST", "SUPER_FAST"];
const classDefenseMultipliers = new Map([ ["relik",0.60], ["bow",0.60], ["wand", 0.80], ["assassin", 1.0], ["spear",1.20] ]);
/*Turns the input amount of skill points into a float precision percentage.
* @param skp - the integer skillpoint count to be converted
*/
@ -39,10 +44,6 @@ function levelToHPBase(level){
}
}
const baseDamageMultiplier = [ 0.51, 0.83, 1.5, 2.05, 2.5, 3.1, 4.3 ];
const attackSpeeds = ["SUPER_SLOW", "VERY_SLOW", "SLOW", "NORMAL", "FAST", "VERY_FAST", "SUPER_FAST"];
/*Class that represents a wynn player's build.
*/
class Build{
@ -133,6 +134,7 @@ class Build{
this.base_skillpoints = result[1];
this.total_skillpoints = result[2];
this.assigned_skillpoints = result[3];
this.activeSetCounts = result[4];
// For strength boosts like warscream, vanish, etc.
this.damageMultiplier = 1.0;
@ -150,14 +152,6 @@ class Build{
/* Get total health for build.
*/
getHealth(){
let health = this.statMap.get("hp") + this.statMap.get("hpBonus");
if(health<5){
return 5;
}else{
return health;
}
}
getSpellCost(spellIdx, cost) {
cost = Math.ceil(cost * (1 - skillPointsToPercentage(this.total_skillpoints[2])));
@ -197,6 +191,43 @@ class Build{
return damages_results.concat([totalDamNorm,totalDamCrit,normDPS,critDPS,avgDPS,adjAtkSpd]);
}
/*
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;
let defMult = classDefenseMultipliers.get(this.weapon.get("type"));
ehp /= ((1-def_pct)*(1-agi_pct)*(2-defMult));
defenseStats.push(ehp);
//HPR
let totalHpr = rawToPct(stats.get("hprRaw"), stats.get("hprPct")/100.);
defenseStats.push(totalHpr);
//EHPR
let ehpr = totalHpr;
ehp /= ((1-def_pct)*(1-agi_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, total hpr, ehpr, [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.
*/
@ -218,7 +249,20 @@ class Build{
statMap.set(id,(statMap.get(id) || 0)+value);
}
for (const staticID of staticIDs) {
if (item.get(staticID)) { statMap.set(staticID, statMap.get(staticID) + item.get(staticID)); }
if (item.get(staticID)) {
statMap.set(staticID, statMap.get(staticID) + item.get(staticID));
}
}
}
for (const [setName, count] of this.activeSetCounts) {
const bonus = sets[setName].bonuses[count-1];
for (const id in bonus) {
if (skp_order.includes(id)) {
// pass. Don't include skillpoints in ids
}
else {
statMap.set(id,(statMap.get(id) || 0)+bonus[id]);
}
}
}

10204
clean.json

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -149,6 +149,10 @@ for item in items:
if item["name"] in item_set_map:
item["set"] = item_set_map[item["name"]]
with open("1_20_ci.json", "r") as ci_file:
ci_items = json.load(ci_file)
items.extend(ci_items)
with open("id_map.json","w") as id_mapfile:
json.dump(id_map, id_mapfile, indent=2)
with open("clean.json", "w") as outfile:

View file

@ -3,3 +3,5 @@ Damage calculator checking: https://its0x7.cf/build/
Theme and overall inspiration: Wynndata (Dukio)
- https://wynndata.tk
Additional Contributors:
- QuantumNep (Layout code/layout ideas)

View file

@ -99,7 +99,7 @@ const spell_table = {
] },
{ title: "Meteor", cost: 8, parts: [
{ subtitle: "Blast Damage", type: "damage", multiplier: 500, conversion: [40, 30, 0, 0, 30, 0] },
{ subtitle: "Burn Damage", type: "damage", multiplier: 125, conversion: [40, 30, 0, 0, 30, 0] },
{ subtitle: "Burn Damage", type: "damage", multiplier: 125, conversion: [100, 0, 0, 0, 0, 0] },
] },
{ title: "Ice Snake", cost: 4, parts: [
{ subtitle: "", type: "damage", multiplier: 70, conversion: [50, 0, 0, 50, 0, 0] },

View file

@ -1,6 +1,6 @@
let nonRolledIDs = ["name", "displayName", "tier", "set", "slots", "type", "material", "drop", "quest", "restrict", "nDam", "fDam", "wDam", "aDam", "tDam", "eDam", "atkSpd", "hp", "fDef", "wDef", "aDef", "tDef", "eDef", "lvl", "classReq", "strReq", "dexReq", "intReq", "defReq", "agiReq","str", "dex", "int", "agi", "def", "fixID", "category", "id", "skillpoints", "reqs", "nDam_", "fDam_", "wDam_", "aDam_", "tDam_", "eDam_"];
let rolledIDs = ["hprPct", "mr", "sdPct", "mdPct", "ls", "ms", "xpb", "lb", "ref", "thorns", "expd", "spd", "atkTier", "poison", "hpBonus", "spRegen", "eSteal", "hprRaw", "sdRaw", "mdRaw", "fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct", "fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct", "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4", "rainbowRaw", "sprint", "sprintReg", "jh", "lq", "gXp", "gSpd"];
let damageClasses = ["Neutral","Earth","Thunder","Water","Fire","Air"];
let reversedIDs = [ "atkTier", "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4" ];
function expandItem(item, powders){
@ -10,24 +10,26 @@ function expandItem(item, powders){
if(item.fixID){ //The item has fixed IDs.
expandedItem.set("fixID",true);
for (const id of rolledIDs){ //all rolled IDs are numerical
let val = (item[id] || 0);
//if(item[id]) {
minRolls.set(id,item[id]);
maxRolls.set(id,item[id]);
minRolls.set(id,val);
maxRolls.set(id,val);
//}
}
}else{ //The item does not have fixed IDs.
for (const id of rolledIDs){
if(item[id] > 0){ // positive rolled IDs
minRolls.set(id,idRound(item[id]*0.3));
maxRolls.set(id,idRound(item[id]*1.3));
}else if(item[id] < 0){ //negative rolled IDs
let val = (item[id] || 0);
if(val > 0){ // positive rolled IDs
minRolls.set(id,idRound(val*0.3));
maxRolls.set(id,idRound(val*1.3));
}else if(val < 0){ //negative rolled IDs
if (reversedIDs.includes(id)) {
maxRolls.set(id,idRound(item[id]*1.3));
minRolls.set(id,idRound(item[id]*0.7));
maxRolls.set(id,idRound(val*1.3));
minRolls.set(id,idRound(val*0.7));
}
else {
minRolls.set(id,idRound(item[id]*1.3));
maxRolls.set(id,idRound(item[id]*0.7));
minRolls.set(id,idRound(val*1.3));
maxRolls.set(id,idRound(val*0.7));
}
}else{//Id = 0
minRolls.set(id,0);
@ -76,6 +78,38 @@ function apply_elemental_format(p_elem, id, suffix) {
p_elem.appendChild(i_elem2);
}
function displaySetBonuses(build, parent_id) {
setHTML(parent_id, "");
let parent_div = document.getElementById(parent_id);
let set_summary_elem = document.createElement('p');
set_summary_elem.classList.add('itemcenter');
set_summary_elem.textContent = "Set Bonuses:";
parent_div.append(set_summary_elem);
for (const [setName, count] of build.activeSetCounts) {
let set_elem = document.createElement('p');
set_elem.id = "set-"+setName;
set_summary_elem.append(set_elem);
const bonus = sets[setName].bonuses[count-1];
let mock_item = new Map();
mock_item.set("fixID", true);
mock_item.set("displayName", setName+" Set: "+count+"/"+sets[setName].items.length);
let mock_minRolls = new Map();
mock_item.set("minRolls", mock_minRolls);
for (const id in bonus) {
if (rolledIDs.includes(id)) {
mock_minRolls.set(id, bonus[id]);
}
else {
mock_item.set(id, bonus[id]);
}
}
displayExpandedItem(mock_item, set_elem.id);
}
}
function displayBuildStats(build, parent_id){
// Commands to "script" the creation of nice formatting.
// #commands create a new element.
@ -119,7 +153,21 @@ function displayBuildStats(build, parent_id){
setHTML(parent_id, "");
let parent_div = document.getElementById(parent_id);
let set_summary_elem = document.createElement('p');
set_summary_elem.classList.add('itemp');
set_summary_elem.classList.add('left');
set_summary_elem.textContent = "Set Summary:";
parent_div.append(set_summary_elem);
for (const [setName, count] of build.activeSetCounts) {
let set_elem = document.createElement('p');
set_elem.classList.add('itemp');
set_elem.classList.add('left');
set_elem.textContent = " "+setName+" Set: "+count+"/"+sets[setName].items.length;
set_summary_elem.append(set_elem);
}
let stats = build.statMap;
console.log(build.statMap);
let active_elem;
let elemental_format = false;
@ -265,11 +313,18 @@ function displayExpandedItem(item, parent_id){
let p_elem = displayFixedID(active_elem, id, item.get(id), elemental_format);
if (id === "slots") {
// HACK TO MAKE POWDERS DISPLAY NICE!! TODO
//let powderMap = new Map([ ["e", "Earth"], ["t", "Thunder"], ["w", "Water"], ["f", "Fire"], ["a", "Air"]]);
p_elem.textContent = idPrefixes[id].concat(item.get(id), idSuffixes[id]) +
" [ " + item.get("powders").map(x => powderNames.get(x)) + " ]";
}else if(id === "displayName"){
p_elem.classList.add("title");
if(item.get("tier") !== " "){
p_elem.classList.add(item.get("tier"));
}
}
}
else if(rolledIDs.includes(id)&& item.get("minRolls").get(id)){ // && item.get("maxRolls").get(id) ){//rolled ID & non-0/non-null/non-und ID
else if (rolledIDs.includes(id) && item.get("minRolls").get(id)){ // && item.get("maxRolls").get(id) ){//rolled ID & non-0/non-null/non-und ID
let style = "positive";
if (item.get("minRolls").get(id) < 0) {
style = "negative";
@ -306,11 +361,14 @@ function displayExpandedItem(item, parent_id){
}//Just don't do anything if else
}
}
let item_desc_elem = document.createElement('p');
item_desc_elem.classList.add('itemp');
item_desc_elem.classList.add('left');
item_desc_elem.textContent = item.get("tier")+" "+item.get("type");
parent_div.append(item_desc_elem);
if (item.get("tier") & item.get("tier") !== " ") {
let item_desc_elem = document.createElement('p');
item_desc_elem.classList.add('itemp');
item_desc_elem.classList.add('left');
item_desc_elem.classList.add(item.get("tier"));
item_desc_elem.textContent = item.get("tier")+" "+item.get("type");
parent_div.append(item_desc_elem);
}
}
function displayFixedID(active, id, value, elemental_format, style) {
@ -351,10 +409,26 @@ function displayFixedID(active, id, value, elemental_format, style) {
return p_elem;
}
}
function displayMeleeDamage(parent_elem, meleeStats){
let attackSpeeds = ["Super Slow", "Very Slow", "Slow", "Normal", "Fast", "Very Fast", "Super Fast"];
let damagePrefixes = ["Neutral Damage: ","Earth Damage: ","Thunder Damage: ","Water Damage: ","Fire Damage: ","Air Damage: "];
function displayEquipOrder(parent_elem,buildOrder){
parent_elem.textContent = "";
const order = buildOrder.slice();
let title_elem = document.createElement("p");
title_elem.textContent = "Equip order ";
title_elem.classList.add("title");
parent_elem.append(title_elem);
for (const item of order) {
let p_elem = document.createElement("p");
p_elem.classList.add("itemp");
p_elem.classList.add("left");
p_elem.textContent = item.get("displayName");
parent_elem.append(p_elem);
}
}
function displayMeleeDamage(parent_elem, overallparent_elem, meleeStats){
let attackSpeeds = ["Super Slow", "Very Slow", "Slow", "Normal", "Fast", "Very Fast", "Super Fast"];
//let damagePrefixes = ["Neutral Damage: ","Earth Damage: ","Thunder Damage: ","Water Damage: ","Fire Damage: ","Air Damage: "];
parent_elem.textContent = "";
overallparent_elem.textContent = "";
const stats = meleeStats.slice();
for (let i = 0; i < 6; ++i) {
@ -373,44 +447,73 @@ function displayMeleeDamage(parent_elem, meleeStats){
//title
let title_elem = document.createElement("p");
title_elem.classList.add("center");
title_elem.classList.add("title");
title_elem.textContent = "Melee Stats";
parent_elem.append(title_elem);
parent_elem.append(document.createElement("br"));
//overall title
let title_elemavg = document.createElement("p");
title_elemavg.classList.add("title");
title_elemavg.textContent = "Melee Stats";
overallparent_elem.append(title_elemavg);
//average DPS
let averageDamage = document.createElement("p");
averageDamage.classList.add("center");
averageDamage.classList.add("itemp");
averageDamage.textContent = "Average DPS: " + stats[10];
parent_elem.append(averageDamage);
//overall average DPS
let overallaverageDamage = document.createElement("p");
overallaverageDamage.classList.add("itemp");
overallaverageDamage.textContent = "Average DPS: " + stats[10];
overallparent_elem.append(overallaverageDamage);
overallparent_elem.append(document.createElement("br"));
//attack speed
let atkSpd = document.createElement("p");
atkSpd.classList.add("center");
atkSpd.classList.add("itemp");
atkSpd.textContent = "Attack Speed: " + attackSpeeds[stats[11]];
parent_elem.append(atkSpd);
parent_elem.append(document.createElement("br"));
//overall attack speed
let overallatkSpd = document.createElement("p");
overallatkSpd.classList.add("center");
overallatkSpd.classList.add("itemp");
overallatkSpd.textContent = "Attack Speed: " + attackSpeeds[stats[11]];
overallparent_elem.append(overallatkSpd);
overallparent_elem.append(document.createElement("br"));
//Non-Crit: n->elem, total dmg, DPS
let nonCritStats = document.createElement("p");
nonCritStats.classList.add("center");
nonCritStats.classList.add("itemp");
nonCritStats.textContent = "Non-Crit Stats: ";
nonCritStats.append(document.createElement("br"));
let dmg = document.createElement("p");
for (let i = 0; i < 6; i++){
if(stats[i][0] > 0){
dmg.textContent = damagePrefixes[i] + stats[i][0] + " - " + stats[i][1];
let dmg = document.createElement("p");
dmg.textContent = stats[i][0] + "-" + stats[i][1];
dmg.classList.add(damageClasses[i]);
dmg.classList.add("itemp");
nonCritStats.append(dmg);
}
}
let normalDamage = document.createElement("p");
normalDamage.textContent = "Total Damage: " + stats[6][0] + " - " + stats[6][1];
normalDamage.textContent = "Total: " + stats[6][0] + "-" + stats[6][1];
normalDamage.classList.add("itemp");
nonCritStats.append(normalDamage);
let normalDPS = document.createElement("p");
normalDPS.textContent = "Normal DPS: " + stats[8];
normalDPS.append(document.createElement("br"));
normalDPS.append(document.createElement("br"));
normalDPS.classList.add("itemp");
nonCritStats.append(normalDPS);
parent_elem.append(nonCritStats);
@ -419,45 +522,128 @@ function displayMeleeDamage(parent_elem, meleeStats){
//Crit: n->elem, total dmg, DPS
let critStats = document.createElement("p");
critStats.classList.add("center");
critStats.classList.add("itemp");
critStats.textContent = "Crit Stats: ";
critStats.append(document.createElement("br"));
dmg = document.createElement("p");
for (let i = 0; i < 6; i++){
if(stats[i][2] > 0){
dmg.textContent = damagePrefixes[i] + stats[i][2] + " - " + stats[i][3];
dmg = document.createElement("p");
dmg.textContent = stats[i][2] + "-" + stats[i][3];
dmg.classList.add(damageClasses[i]);
dmg.classList.add("itemp");
critStats.append(dmg);
}
}
normalDamage = document.createElement("p");
normalDamage.textContent = "Total Damage: " + stats[7][0] + " - " + stats[7][1];
critStats.append(normalDamage);
let critDamage = document.createElement("p");
critDamage.textContent = "Total: " + stats[7][0] + "-" + stats[7][1];
critDamage.classList.add("itemp");
critStats.append(critDamage);
normalDPS = document.createElement("p");
normalDPS.textContent = "Crit DPS: " + stats[9];
normalDPS.append(document.createElement("br"));
normalDPS.append(document.createElement("br"));
critStats.append(normalDPS);
let critDPS = document.createElement("p");
critDPS.textContent = "Crit DPS: " + stats[9];
critDPS.classList.add("itemp");
critDPS.append(document.createElement("br"));
critDPS.append(document.createElement("br"));
critStats.append(critDPS);
parent_elem.append(critStats);
}
function displayDefenseStats(parent_elem,defenseStats){
parent_elem.textContent = "";
const stats = defenseStats.slice();
let title_elem = document.createElement("p");
title_elem.textContent = "Defense Stats";
title_elem.classList.add("title");
parent_elem.append(title_elem);
parent_elem.append(document.createElement("br"));
//[total hp, ehp, total hpr, ehpr, [def%, agi%], [edef,tdef,wdef,fdef,adef]]
for(const i in stats){
if(typeof stats[i] === "number"){
stats[i] = stats[i].toFixed(2);
}else{
for(const j in stats[i]){
stats[i][j] = stats[i][j].toFixed(2);
}
}
}
//total HP
let hpElem = document.createElement("p");
hpElem.textContent = "HP: " + stats[0];
hpElem.classList.add("left");
hpElem.classList.add("Health");
parent_elem.append(hpElem);
//EHP
let ehpElem = document.createElement("p");
ehpElem.textContent = "Effective HP: " + stats[1];
ehpElem.classList.add("left");
parent_elem.append(ehpElem);
//total HPR
let hprElem = document.createElement("p");
hprElem.textContent = "HP Regen: " + stats[2];
hprElem.classList.add("left");
hprElem.classList.add("Health");
parent_elem.append(hprElem);
//EHPR
let ehprElem = document.createElement("p");
ehprElem.textContent = "Effective HP Regen: " + stats[3];
ehprElem.classList.add("left");
parent_elem.append(ehprElem);
//eledefs
let eledefs = stats[5];
for (let i = 0; i < eledefs.length; i++){
/* TODO: make this comment work
let eledefElem = document.createElement("p");
let ele = document.createElement("b");
ele.classList.add(damageClasses[i+1]);
ele.textContent = damageClasses[i+1];
eledefElem.textContent = " Defense: " + eledefs[i];
//eledefElem.classList.add(damageClasses[i+1]);
eledefElem.classList.add("left");
parent_elem.append(ele);
parent_elem.append(eledefElem);
*/
let eledefElem = document.createElement("p");
eledefElem.textContent = damageClasses[i+1] + " Defense: " + eledefs[i];
eledefElem.classList.add(damageClasses[i+1]);
eledefElem.classList.add("left");
parent_elem.append(eledefElem);
}
//skp
let defElem = document.createElement("p");
defElem.textContent = "Damage Absorbed %: " + stats[4][0] + "%";
defElem.classList.add("left");
parent_elem.append(defElem);
let agiElem = document.createElement("p");
agiElem.textContent = "Dodge Chance %: " + stats[4][1] + "%";
agiElem.classList.add("left");
parent_elem.append(agiElem);
}
function displaySpellDamage(parent_elem, build, spell, spellIdx) {
function displaySpellDamage(parent_elem, overallparent_elem, build, spell, spellIdx) {
parent_elem.textContent = "";
const stats = build.statMap;
let title_elem = document.createElement("p");
title_elem.classList.add('center');
title_elem.classList.add('title');
overallparent_elem.textContent = "";
let title_elemavg = document.createElement("p");
title_elemavg.classList.add('title');
if (spellIdx != 0) {
title_elem.textContent = spell.title + " (" + build.getSpellCost(spellIdx, spell.cost) + ")";
title_elemavg.textContent = spell.title + " (" + build.getSpellCost(spellIdx, spell.cost) + ")";
}
else {
title_elem.textContent = spell.title;
title_elemavg.textContent = spell.title;
}
parent_elem.append(title_elem);
overallparent_elem.append(title_elemavg);
let critChance = skillPointsToPercentage(build.total_skillpoints[1]);
let save_damages = [];
@ -467,9 +653,17 @@ function displaySpellDamage(parent_elem, build, spell, spellIdx) {
let part_div = document.createElement("p");
parent_elem.append(part_div);
let part_divavg = document.createElement("p");
overallparent_elem.append(part_divavg);
let subtitle_elem = document.createElement("p");
subtitle_elem.textContent = part.subtitle;
part_div.append(subtitle_elem);
let subtitle_elemavg = document.createElement("p");
subtitle_elemavg.textContent = part.subtitle;
part_divavg.append(subtitle_elemavg);
if (part.type === "damage") {
let _results = calculateSpellDamage(stats, part.conversion,
@ -492,12 +686,17 @@ function displaySpellDamage(parent_elem, build, spell, spellIdx) {
averageLabel.classList.add("damageSubtitle");
part_div.append(averageLabel);
let overallaverageLabel = document.createElement("p");
overallaverageLabel.textContent = "Average: "+averageDamage.toFixed(2);
overallaverageLabel.classList.add("damageSubtitle");
part_divavg.append(overallaverageLabel);
let nonCritLabel = document.createElement("p");
nonCritLabel.textContent = "Non-Crit Average: "+nonCritAverage.toFixed(2);
nonCritLabel.classList.add("damageSubtitle");
part_div.append(nonCritLabel);
let damageClasses = ["Neutral","Earth","Thunder","Water","Fire","Air"];
for (let i = 0; i < 6; i++){
if (results[i][1] > 0){
let p = document.createElement("p");
@ -525,11 +724,17 @@ function displaySpellDamage(parent_elem, build, spell, spellIdx) {
save_damages.push(averageDamage);
}
else if (part.type == "heal") {
let heal_amount = part.strength * build.getHealth() * Math.max(0, Math.min(1.5, 1 + 0.05 * stats.get("wDamPct")));
let heal_amount = (part.strength * build.getDefenseStats()[0] * Math.max(0, Math.min(1.5, 1 + 0.05 * stats.get("wDamPct")))).toFixed(2);
let healLabel = document.createElement("p");
healLabel.textContent = heal_amount;
healLabel.classList.add("damagep");
part_div.append(healLabel);
let overallhealLabel = document.createElement("p");
overallhealLabel.textContent = heal_amount;
overallhealLabel.classList.add("damagep")
part_divavg.append(overallhealLabel);
}
else if (part.type === "total") {
let total_damage = 0;
@ -540,6 +745,12 @@ function displaySpellDamage(parent_elem, build, spell, spellIdx) {
averageLabel.textContent = "Average: "+total_damage.toFixed(2);
averageLabel.classList.add("damageSubtitle");
part_div.append(averageLabel);
let overallaverageLabel = document.createElement("p");
overallaverageLabel.textContent = "Average: "+total_damage.toFixed(2);
overallaverageLabel.classList.add("damageSubtitle");
part_divavg.append(averageLabel);
}
}
}

View file

@ -22,118 +22,152 @@
<a href="credits.txt" class="link">Additional credits</a>
</div>
<div class="equipment">
<div class="right" style="grid-column:1;grid-row:1">
<label>Equipments</label>
<br>
<br>
<div>
<label for="helmet-choice">Helmet:</label>
<input list="helmet-items" id="helmet-choice" name="helmet-choice" placeholder="No Helmet"/>
<datalist id="helmet-items">
</datalist>
</div>
<br>
<div>
<label for="chestplate-choice">Chestplate:</label>
<input list="chestplate-items" id="chestplate-choice" name="chestplate-choice" placeholder="No Chestplate"/>
<datalist id="chestplate-items">
</datalist>
</div>
<br>
<div>
<label for="leggings-choice">Leggings:</label>
<input list="leggings-items" id="leggings-choice" name="leggings-choice" placeholder="No Leggings"/>
<datalist id="leggings-items">
</datalist>
</div>
<br>
<div id="boots">
<label for="boots-choice">Boots:</label>
<input list="boots-items" id="boots-choice" name="boots-choice" placeholder="No Boots"/>
<datalist id="boots-items">
</datalist>
</div>
<br>
<div>
<label for="ring1-choice">Ring 1:</label>
<input list="ring1-items" id="ring1-choice" name="ring1-choice" placeholder="No Ring 1"/>
<datalist id="ring1-items">
</datalist>
</div>
<br>
<div>
<label for="ring2-choice">Ring 2:</label>
<input list="ring2-items" id="ring2-choice" name="ring2-choice" placeholder="No Ring 2"/>
<datalist id="ring2-items">
</datalist>
</div>
<br>
<div>
<label for="bracelet-choice">Bracelet:</label>
<input list="bracelet-items" id="bracelet-choice" name="bracelet-choice" placeholder="No Bracelet"/>
<datalist id="bracelet-items">
</datalist>
</div>
<br>
<div>
<label for="necklace-choice">Necklace:</label>
<input list="necklace-items" id="necklace-choice" name="necklace-choice" placeholder="No Necklace"/>
<datalist id="necklace-items">
</datalist>
</div>
<br>
<div>
<label for="weapon-choice">Weapon:</label>
<input list="weapon-items" id="weapon-choice" name="weapon-choice" value=""/>
<datalist id="weapon-items">
</datalist>
</div>
<br>
<div>
<button class = "button" id = "calc-button" onclick = "calculateBuild()">
Update Items (Resets stats)
</button>
</div>
</div>
<div class="left" style="grid-column:2;grid-row:1">
<label>Powdering:</label>
<div id="helmet-slots">
X slots
</div>
<div>
<input type="text" id="helmet-powder" name="helmet-powder" />
</div>
<div id="chestplate-slots">
X slots
</div>
<div>
<input type="text" id="chestplate-powder" name="chestplate-powder" />
</div>
<div id="leggings-slots">
X slots
</div>
<div>
<input type="text" id="leggings-powder" name="leggings-powder" />
</div>
<div id="boots-slots">
X slots
</div>
<div>
<input type="text" id="boots-powder" name="boots-powder" />
</div>
<br/><br/><br/><br/><br/><br/><br/><br/>
<div id="weapon-slots">
X slots
</div>
<div>
<input type="text" id="weapon-powder" name="weapon-powder" />
</div>
<br>
<div>
<button class = "reset" id = "reset-button" onclick = "resetFields()">
Reset
</button>
</div>
<div class="left" style="grid-column:1/span 2;grid-row:1">
<table>
<tr>
<th class="right">
<label>Equipments</label>
</th>
<th>
<label>Powdering</label>
</th>
</tr>
<tr>
<td class="right">
<br/>
<label for="helmet-choice">Helmet:</label>
<input list="helmet-items" id="helmet-choice" name="helmet-choice" placeholder="No Helmet"/>
<datalist id="helmet-items">
</datalist>
</td>
<td>
<div id="helmet-slots">
X slots
</div>
<div>
<input type="text" id="helmet-powder" name="helmet-powder"/>
</div>
</td>
</tr>
<tr>
<td class="right">
<br/>
<label for="chestplate-choice">Chestplate:</label>
<input list="chestplate-items" id="chestplate-choice" name="chestplate-choice" placeholder="No Chestplate" />
<datalist id="chestplate-items">
</datalist>
</td>
<td>
<div id="chestplate-slots">
X slots
</div>
<div>
<input type="text" id="chestplate-powder" name="chestplate-powder" />
</div>
</td>
</tr>
<tr>
<td class="right">
<br/>
<label for="leggings-choice">Leggings:</label>
<input list="leggings-items" id="leggings-choice" name="leggings-choice" placeholder="No Leggings" />
<datalist id="leggings-items">
</datalist>
</td>
<td>
<div id="leggings-slots">
X slots
</div>
<div>
<input type="text" id="leggings-powder" name="leggings-powder" />
</div>
</td>
</tr>
<tr>
<td class="right">
<br/>
<label for="boots-choice">Boots:</label>
<input list="boots-items" id="boots-choice" name="boots-choice" placeholder="No Boots" />
<datalist id="boots-items">
</datalist>
</td>
<td>
<div id="boots-slots">
X slots
</div>
<div style="grid-column:1;grid-row:4">
<input type="text" id="boots-powder" name="boots-powder"/>
</div>
</td>
</tr>
<tr>
<td class="right">
<br/>
<label for="ring1-choice">Ring 1:</label>
<input list="ring1-items" id="ring1-choice" name="ring1-choice" placeholder="No Ring 1"/>
<datalist id="ring1-items">
</datalist>
</td>
</tr>
<tr>
<td class="right">
<br/>
<label for="ring2-choice">Ring 2:</label>
<input list="ring2-items" id="ring2-choice" name="ring2-choice" placeholder="No Ring 2" />
<datalist id="ring2-items">
</datalist>
</td>
</tr>
<tr>
<td class="right">
<br/>
<label for="bracelet-choice">Bracelet:</label>
<input list="bracelet-items" id="bracelet-choice" name="bracelet-choice" placeholder="No Bracelet" />
<datalist id="bracelet-items">
</datalist>
</td>
</tr>
<tr>
<td class="right">
<br/>
<label for="necklace-choice">Necklace:</label>
<input list="necklace-items" id="necklace-choice" name="necklace-choice" placeholder="No Necklace"/>
<datalist id="necklace-items">
</datalist>
</td>
</tr>
<tr>
<td class="right">
<br/>
<label for="weapon-choice">Weapon:</label>
<input list="weapon-items" id="weapon-choice" name="weapon-choice" placeholder="No Weapon" value=""/>
<datalist id="weapon-items">
</datalist>
</td>
<td>
<div id="weapon-slots">
X slots
</div>
<div>
<input type="text" id="weapon-powder" name="weapon-powder" />
</div>
</td>
</tr>
<tr>
<td>
<br/>
<button class = "button" id = "calc-button" onclick = "calculateBuild()">
Update Items (Resets stats)
</button>
</td>
<td>
<br/>
<button class = "reset" id = "reset-button" onclick = "resetFields()">
Reset
</button>
</td>
</tr>
</table>
</div>
<div class="center" style="grid-column:3;grid-row:1">
<div class = "center build-overall" id = "build-overall">
@ -141,6 +175,23 @@
<div class = "center" id = "build-overall-stats"></div>
</div>
</div>
<div class="center" style="grid-column:4;">
<div class="spell-info" style="grid-row:1;">
<div class="center" id="build-melee-statsAvg">melee</div>
</div>
<div class="spell-info" style="grid-row:1;">
<div class="center" id="spell0-infoAvg">spell1</div>
</div>
<div class="spell-info" style="grid-row:2">
<div class="center" id="spell1-infoAvg">spell2</div>
</div>
<div class="spell-info" style="grid-row:3">
<div class="center" id="spell2-infoAvg">spell3</div>
</div>
<div class="spell-info" style="grid-row:4">
<div class="center" id="spell3-infoAvg">spell4</div>
</div>
</div>
</div>
<div class="center" id="summary-box">
Summary:
@ -250,16 +301,12 @@
<div class = "center build-weapon" id = "build-weapon" style = "grid-column:1;grid-row:3">
<div class = "center" id = "build-weapon-stats"></div>
</div>
<div class = "center build-overall" id = "build-overall" style = "grid-column:4;grid-row:3">
<p class="itemcenter">Overall Build Stats:<p>
<div class = "center" id = "build-overall-stats"></div>
<div class = "center build-order" id = "build-order" style = "grid-column:2;grid-row:3">
</div>
<div class = "center build-melee-stats" id = "build-melee-stats" style = "grid-column:3;grid-row:3">
</div>
<div class = "center build-order" id = "build-order" style = "grid-column:2;grid-row:3">
<div class = "center build-defense-stats" id = "build-defense-stats" style = "grid-column:4;grid-row:3">
</div>
<!--div class = "center" id = "build-defense-stats" style = "grid-column:4;grid-row:3">
</div-->
</div>
<div class = "spells">
<div class = "center spell-info" id = "spell0" style = "grid-column:1;grid-row:1">
@ -279,6 +326,11 @@
<div class = "center" id = "spell3-info">Spell 4</div>
</div>
</div>
<div class="misc">
<div class = "center set-info" id = "set-info-div" style = "grid-column:1;grid-row:1">
<div class = "center" id = "set-info">Set info</div>
</div>
</div>
<script type="text/javascript" src="utils.js"></script>
<script type="text/javascript" src="skillpoints.js"></script>

View file

@ -1,4 +1,4 @@
const DB_VERSION = 7;
const DB_VERSION = 11;
// @See https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/video-store/index.js
let db;

View file

@ -69,93 +69,153 @@ for item in build_items:
setup(item)
if all(x == 0 for x in item["reqs"]):
fixed.append(item)
elif all(x == 0 for x in item["skillpoints"]):
elif all(x == 0 for x in item["skillpoints"]) and item["set"] is None:
noboost.append(item)
else:
consider.append(item)
setup(build_weapon)
fixed = tuple(fixed)
noboost = tuple(noboost)
consider = tuple(consider)
"""
The way this code expects this to work:
sets is a map: setName -> setObject {
bonuses: array[bonusObject]
}
where each bonusObject is a mapping from id to boost value.
And the bonuses array describes the effect of equipping set items (0 index = 1 item).
"""
sets = dict()
# Apply the skillpoints an item gives to the build.
def apply_skillpoints(skillpoints, item):
"""
skillPoints: current skillpoint totals.
item: Item in uestion.
activeSetCounts: Mapping from setname to number of items currently worn (not including this one).
"""
def apply_skillpoints(skillpoints, item, activeSetCounts):
for i in range(5):
skillpoints[i] += item["skillpoints"][i]
def remove_skillpoints(skillpoints, item):
for i in range(5):
skillpoints[i] -= item["skillpoints"][i]
if item["set"] is not None:
setName = item["set"]
old_bonus = dict()
if setName in activeSetCounts:
setCount = activeSetCounts[setName]
old_bonus = sets[setName]["bonuses"][setCount-1]
activeSetCounts[setName] = setCount + 1
else:
setCount = 0
activeSetCounts[setName] = 1
new_bonus = sets[setName]["bonuses"][setCount]
skp_order = ["str","dex","int","def","agi"]
for i, skp in enumerate(skp_order):
delta = new_bonus[skp] - old_bonus[skp]
skillpoints[i] += delta
# Figure out (naively) how many skillpoints need to be applied to get the current item to fit.
# Doesn't handle -skp.
def apply_to_fit(skillpoints, item):
def apply_to_fit(skillpoints, item, skillpoint_filter, activeSetCounts):
applied = [0, 0, 0, 0, 0]
total = 0
for i, req, cur in zip(range(5), item["reqs"], skillpoints):
if item["skillpoints"][i] < 0 and skillpoint_filter[i]:
applied[i] -= item["skillpoints"][i]
total -= item["skillpoints"][i]
if (item["reqs"][i] == 0):
continue
skillpoint_filter[i] = True
if req > cur:
diff = req - cur
applied[i] += diff
total += diff
if item["set"] is not None:
setName = item["set"]
old_bonus = dict()
if setName in activeSetCounts:
setCount = activeSetCounts[setName]
old_bonus = sets[setName]["bonuses"][setCount-1]
activeSetCounts[setName] = setCount + 1
else:
setCount = 0;
activeSetCounts[setName] = 1
new_bonus = sets[setName]["bonuses"][setCount]
skp_order = ["str","dex","int","def","agi"]
for i, skp in enumerate(skp_order):
delta = new_bonus[skp] - old_bonus[skp]
if delta < 0 and skillpoint_filter[i]:
applied[i] -= delta
total -= delta
return applied, total
# Permutations in js reference (also cool algorithm):
# https://stackoverflow.com/a/41068709
static_skillpoints_base = [0, 0, 0, 0, 0]
static_activeSetCounts = dict()
# Separate out the no req items and add them to the static skillpoint base.
for item in fixed:
apply_skillpoints(static_skillpoints_base, item)
apply_skillpoints(static_skillpoints_base, item, static_activeSetCounts)
best = None
final_skillpoints = None
best = consider + noboost;
final_skillpoints = static_skillpoints_base[:]
best_skillpoints = [0, 0, 0, 0, 0]
best_total = math.inf
best_activeSetCounts = dict()
# Try every combination and pick the best one.
import itertools
for permutation in itertools.permutations(consider):
allFalse = [False] * 5
permutation += noboost
if len(consider) or len(noboost):
skillpoints_applied = [0, 0, 0, 0, 0]
skillpoints = copy.copy(static_skillpoints_base)
total_applied = 0
for item in permutation:
needed_skillpoints, total_diff = apply_to_fit(skillpoints, item)
for i in range(5):
skillpoints_applied[i] += needed_skillpoints[i]
skillpoints[i] += needed_skillpoints[i]
apply_skillpoints(skillpoints, item)
total_applied += total_diff
if total_applied >= best_total:
break
if total_applied < best_total:
# Try every combination and pick the best one.
import itertools
for permutation in itertools.permutations(consider):
activeSetCounts = dict(best_activeSetCounts)
has_skillpoint = allFalse[:]
permutation += noboost
skillpoints_applied = [0, 0, 0, 0, 0]
skillpoints = static_skillpoints_base[:]
total_applied = 0
for item in permutation:
remove_skillpoints(skillpoints, item)
needed_skillpoints, total_diff = apply_to_fit(skillpoints, item)
needed_skillpoints, total_diff = apply_to_fit(skillpoints, item, has_skillpoint, activeSetCounts)
for i in range(5):
skillpoints_applied[i] += needed_skillpoints[i]
skillpoints[i] += needed_skillpoints[i]
apply_skillpoints(skillpoints, item)
apply_skillpoints(skillpoints, item, activeSetCounts)
total_applied += total_diff
if total_applied >= best_total:
break
needed_skillpoints, total_diff = apply_to_fit(skillpoints, build_weapon)
for i in range(5):
skillpoints_applied[i] += needed_skillpoints[i]
skillpoints[i] += needed_skillpoints[i]
apply_skillpoints(skillpoints, build_weapon)
total_applied += total_diff
needed_skillpoints, total_diff = apply_to_fit(skillpoints, build_weapon, has_skillpoint, activeSetCounts)
for i in range(5):
skillpoints_applied[i] += needed_skillpoints[i]
skillpoints[i] += needed_skillpoints[i]
apply_skillpoints(skillpoints, build_weapon, activeSetCounts)
total_applied += total_diff
if total_applied < best_total:
best = permutation
final_skillpoints = skillpoints
best_skillpoints = skillpoints_applied
best_total = total_applied
if total_applied < best_total:
best = permutation
final_skillpoints = skillpoints
best_skillpoints = skillpoints_applied
best_total = total_applied
best_activeSetCounts = activeSetCounts
else:
best_total = 0
needed_skillpoints, total_diff = apply_to_fit(skillpoints, build_weapon, allFalse, best_activeSetCounts)
for i in range(5):
best_skillpoints[i] += needed_skillpoints[i]
final_skillpoints[i] += needed_skillpoints[i]
apply_skillpoints(skillpoints, build_weapon, best_activeSetCounts)
best_total += total_diff
equip_order = fixed + best
results = [equip_order, best_skillpoints, final_skillpoints, best_total, best_activeSetCounts];
print([i["displayName"] for i in fixed + best])
print(best_skillpoints)

View file

@ -10,32 +10,45 @@ function calculate_skillpoints(equipment, weapon) {
if (item.get("reqs").every(x => x === 0)) {
fixed.push(item);
}
else if (item.get("skillpoints").every(x => x === 0)) {
// TODO hack: We will treat ALL set items as unsafe :(
else if (item.get("skillpoints").every(x => x === 0) && item.get("set") === null) {
noboost.push(item);
}
else {
consider.push(item);
}
}
function apply_skillpoints(skillpoints, item) {
function apply_skillpoints(skillpoints, item, activeSetCounts) {
for (let i = 0; i < 5; i++) {
skillpoints[i] += item.get("skillpoints")[i];
}
}
function remove_skillpoints(skillpoints, item) {
for (let i = 0; i < 5; i++) {
skillpoints[i] -= item.get("skillpoints")[i];
const setName = item.get("set");
if (setName) { // undefined/null means no set.
let setCount = activeSetCounts.get(setName);
let old_bonus = {};
if (setCount) {
old_bonus = sets[setName].bonuses[setCount-1];
activeSetCounts.set(setName, setCount + 1);
}
else {
setCount = 0;
activeSetCounts.set(setName, 1);
}
const new_bonus = sets[setName].bonuses[setCount];
//let skp_order = ["str","dex","int","def","agi"];
for (const i in skp_order) {
const delta = (new_bonus[skp_order[i]] || 0) - (old_bonus[skp_order[i]] || 0);
skillpoints[i] += delta;
}
}
}
// Figure out (naively) how many skillpoints need to be applied to get the current item to fit.
// Doesn't handle -skp.
function apply_to_fit(skillpoints, item, skillpoint_filter) {
function apply_to_fit(skillpoints, item, skillpoint_filter, activeSetCounts) {
let applied = [0, 0, 0, 0, 0];
let total = 0;
for (let i = 0; i < 5; i++) {
if (item.get("skillpoints")[i] < 0 && skillpoint_filter[i] === true) {
if (item.get("skillpoints")[i] < 0 && skillpoint_filter[i]) {
applied[i] -= item.get("skillpoints")[i];
total -= item.get("skillpoints")[i];
}
@ -49,24 +62,46 @@ function calculate_skillpoints(equipment, weapon) {
total += diff;
}
}
const setName = item.get("set");
if (setName) { // undefined/null means no set.
const setCount = activeSetCounts.get(setName);
if (setCount) {
const old_bonus = sets[setName].bonuses[setCount-1];
const new_bonus = sets[setName].bonuses[setCount];
//let skp_order = ["str","dex","int","def","agi"];
for (const i in skp_order) {
const delta = (new_bonus[skp_order[i]] || 0) - (old_bonus[skp_order[i]] || 0);
if (delta < 0 && skillpoint_filter[i]) {
applied[i] -= delta;
total -= delta;
}
}
}
}
return [applied, total];
}
// Separate out the no req items and add them to the static skillpoint base.
let static_skillpoints_base = [0, 0, 0, 0, 0]
let static_activeSetCounts = new Map()
for (const item of fixed) {
apply_skillpoints(static_skillpoints_base, item);
apply_skillpoints(static_skillpoints_base, item, static_activeSetCounts);
}
let best = consider.concat(noboost);
let final_skillpoints = static_skillpoints_base.slice();
let best_skillpoints = [0, 0, 0, 0, 0];
let best_total = Infinity;
let best_activeSetCounts = static_activeSetCounts;
let allFalse = [false, false, false, false, false];
if (consider.length > 0 || noboost.length > 0) {
// Try every combination and pick the best one.
for (let permutation of perm(consider)) {
let activeSetCounts = new Map(static_activeSetCounts);
let has_skillpoint = allFalse.slice();
permutation = permutation.concat(noboost);
@ -81,7 +116,7 @@ function calculate_skillpoints(equipment, weapon) {
let needed_skillpoints;
let total_diff;
for (const item of permutation) {
result = apply_to_fit(skillpoints, item, has_skillpoint);
result = apply_to_fit(skillpoints, item, has_skillpoint, activeSetCounts);
needed_skillpoints = result[0];
total_diff = result[1];
@ -89,40 +124,14 @@ function calculate_skillpoints(equipment, weapon) {
skillpoints_applied[i] += needed_skillpoints[i];
skillpoints[i] += needed_skillpoints[i];
}
apply_skillpoints(skillpoints, item);
apply_skillpoints(skillpoints, item, activeSetCounts);
total_applied += total_diff;
if (total_applied >= best_total) {
break;
}
}
// if (total_applied < best_total) {
// console.log(total_applied);
// console.log(skillpoints_applied);
// console.log("Iteration 2");
// for (const item of permutation) {
// console.log(item);
//
// remove_skillpoints(skillpoints, item);
// console.log(skillpoints);
// result = apply_to_fit(skillpoints, item, has_skillpoint);
// needed_skillpoints = result[0];
// total_diff = result[1];
// for (let i = 0; i < 5; ++i) {
// skillpoints_applied[i] += needed_skillpoints[i];
// skillpoints[i] += needed_skillpoints[i];
// }
//
// apply_skillpoints(skillpoints, item);
// console.log(skillpoints);
// console.log(total_diff);
// total_applied += total_diff;
// if (total_applied >= best_total) {
// break;
// }
// }
// }
let pre = skillpoints.slice();
result = apply_to_fit(skillpoints, weapon, allFalse.slice());
result = apply_to_fit(skillpoints, weapon, allFalse.slice(), activeSetCounts);
needed_skillpoints = result[0];
total_diff = result[1];
for (let i = 0; i < 5; ++i) {
@ -130,32 +139,31 @@ function calculate_skillpoints(equipment, weapon) {
skillpoints[i] += needed_skillpoints[i];
}
apply_skillpoints(skillpoints, weapon);
apply_skillpoints(skillpoints, weapon, activeSetCounts);
total_applied += total_diff;
if (total_applied < best_total) {
console.log(pre);
console.log(skillpoints);
best = permutation;
final_skillpoints = skillpoints;
best_skillpoints = skillpoints_applied;
best_total = total_applied;
best_activeSetCounts = activeSetCounts;
}
}
}
else {
best_total = 0;
result = apply_to_fit(final_skillpoints, weapon, allFalse.slice());
result = apply_to_fit(final_skillpoints, weapon, allFalse.slice(), best_activeSetCounts);
needed_skillpoints = result[0];
total_diff = result[1];
for (let i = 0; i < 5; ++i) {
best_skillpoints[i] += needed_skillpoints[i];
final_skillpoints[i] += needed_skillpoints[i];
}
apply_skillpoints(final_skillpoints, weapon);
apply_skillpoints(final_skillpoints, weapon, best_activeSetCounts);
best_total += total_diff;
}
let equip_order = fixed.concat(best);
return [equip_order, best_skillpoints, final_skillpoints, best_total];
return [equip_order, best_skillpoints, final_skillpoints, best_total, best_activeSetCounts];
}

View file

@ -8,6 +8,10 @@
font-weight: 700;
}
th, td {
padding-right: 20px;
}
.equipment {
padding: 4%;
display: grid;
@ -24,7 +28,7 @@
grid-auto-rows: minmax(60px, auto);
}
.equipment, .skillpoints, .center, .header, .all{
background: #110110;
background: #121516;
color: #aaa;
}
.hppeng{
@ -36,6 +40,10 @@
a.link{
color: #A5FDFF;
}
.title{
text-align: center;
font-size: 150%;
}
.center {
text-align: center;
}
@ -45,25 +53,27 @@ a.link{
}
.left {
margin: 2px 2%;
text-align: left;
}
.build, .spells {
.build, .spells, .misc {
padding: 2%;
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
grid-auto-rows: minmax(60px, auto);
width: 94%;
background: #110110;
background: #121516;
}
.build-helmet, .build-chestplate, .build-leggings, .build-boots, .build-ring1, .build-ring2, .build-bracelet, .build-necklace, .build-weapon, .build-order, .build-overall, .build-melee-stats, .spell-info {
.build-helmet, .build-chestplate, .build-leggings, .build-boots, .build-ring1, .build-ring2, .build-bracelet, .build-necklace, .build-weapon, .build-order, .build-overall, .build-melee-stats, .build-defense-stats, .spell-info, .set-info {
color: #aaa;
background: #110110;
background: #121516;
border: 3px solid #BCBCBC;
border-radius: 3px;
width: 96%;
margin-bottom: 10px;
}
.itemcenter {
@ -172,20 +182,68 @@ a.link{
width: 10px;
}
/* Track */
::-webkit-scrollbar-track {
/* Track */
::-webkit-scrollbar-track {
box-shadow: inset 0 0 5px #BCBCBC;
border: #BCBCBC;
border-radius: 5px;
}
}
/* Handle */
::-webkit-scrollbar-thumb {
/* Handle */
::-webkit-scrollbar-thumb {
background: #aaa;
border-radius: 10px;
}
}
/* Ugly Corner */
::-webkit-scrollbar-corner{
background: #110110;
}
/* Ugly Corner */
::-webkit-scrollbar-corner{
background: #110110;
}
button {
background-color: #666;
border: 2px solid #444;
border-radius: 5px;
color: #ddd;
text-align: center;
text-decoration: none;
font-family: 'Nunito',sans-serif;
font-weight: 700;
font-size: 120%;
display: inline-block;
}
input {
background-color: #666;
border: 2px solid #444;
border-radius: 5px;
color: #ddd;
text-align: center;
text-decoration: none;
font-family: 'Nunito',sans-serif;
font-weight: 700;
display: inline-block;
}
::placeholder{
color: #ddd;
}
/* Tier colors tier colors */
.Normal{
color: #fff;
}
.Unique{
color:#ff5;
}
.Rare{
color:#f5f;
}
.Legendary{
color:#5ff;
}
.Fabled{
color:#f55;
}
.Mythic{
color:#a0a;
}
.Set{
color:#5f5
}

31
test.js
View file

@ -11,7 +11,7 @@ console.log(url_tag);
* END testing section
*/
const BUILD_VERSION = "3.2";
const BUILD_VERSION = "4.6";
document.getElementById("header").textContent = "Wynn build calculator "+BUILD_VERSION+" (db version "+DB_VERSION+")";
@ -24,6 +24,7 @@ let weaponTypes = [ "wand", "spear", "bow", "dagger", "relik" ];
let item_fields = [ "name", "displayName", "tier", "set", "slots", "type", "material", "drop", "quest", "restrict", "nDam", "fDam", "wDam", "aDam", "tDam", "eDam", "atkSpd", "hp", "fDef", "wDef", "aDef", "tDef", "eDef", "lvl", "classReq", "strReq", "dexReq", "intReq", "defReq", "agiReq", "hprPct", "mr", "sdPct", "mdPct", "ls", "ms", "xpb", "lb", "ref", "str", "dex", "int", "agi", "def", "thorns", "expd", "spd", "atkTier", "poison", "hpBonus", "spRegen", "eSteal", "hprRaw", "sdRaw", "mdRaw", "fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct", "fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct", "fixID", "category", "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4", "rainbowRaw", "sprint", "sprintReg", "jh", "lq", "gXp", "gSpd", "id" ];
let skp_order = ["str","dex","int","def","agi"];
let skp_elements = ["e","t","w","f","a"];
let skpReqs = skp_order.map(x => x + "Req");
let equipment_fields = [
@ -54,7 +55,7 @@ let buildFields = equipment_fields.map(x => "build-"+x);
let powderIDs = new Map();
let powderNames = new Map();
let _powderID = 0;
for (const x of ['e', 't', 'w', 'f', 'a']) {
for (const x of skp_elements) {
for (let i = 1; i <= 6; ++i) {
// Support both upper and lowercase, I guess.
powderIDs.set(x.toUpperCase()+i, _powderID);
@ -80,7 +81,7 @@ class Powder {
this.defMinus = defMinus;
}
}
function _p(a,b,c,d,e) { return new Powder(a,b,c,d,e); }
function _p(a,b,c,d,e) { return new Powder(a,b,c,d,e); } //bruh moment
let powderStats = [
_p(3,6,17,2,1), _p(6,9,21,4,2), _p(8,14,25,8,3), _p(11,16,31,14,5), _p(15,18,38,22,9), _p(18,22,46,30,13),
@ -96,6 +97,7 @@ for (const it of itemTypes) {
itemLists.set(it, []);
}
let itemMap = new Map();
/* Mapping from item names to set names. */
let idMap = new Map();
/*
@ -124,13 +126,11 @@ function init() {
["accessory", "ring", "No Ring 2"],
["accessory", "bracelet", "No Bracelet"],
["accessory", "necklace", "No Necklace"],
["weapon", "wand", "No Weapon"],
["weapon", "dagger", "No Weapon"],
];
for (let i = 0; i < 9; i++) {
let item = Object();
for (const field of item_fields) {
item[field] = 0;
}
item.slots = 0;
item.category = noneItems[i][0];
item.type = noneItems[i][1];
item.name = noneItems[i][2];
@ -353,12 +353,9 @@ function calculateBuild(save_skp, skp){
console.log(equipment);
player_build = new Build(106, equipment, powderings);
console.log(player_build.toString());
displayEquipOrder(document.getElementById("build-order"),player_build.equip_order);
let equip_order_text = "Equip order: <br>";
for (const item of player_build.equip_order) {
equip_order_text += item.get("displayName") + "<br>";
}
setHTML("build-order", equip_order_text);
const assigned = player_build.base_skillpoints;
const skillpoints = player_build.total_skillpoints;
@ -423,10 +420,13 @@ function calculateBuildStats() {
}
displayBuildStats(player_build, "build-overall-stats");
displaySetBonuses(player_build, "set-info");
let parent_elem = document.getElementById("build-melee-stats");
let meleeStats = player_build.getMeleeStats();
displayMeleeDamage(parent_elem,meleeStats);
displayMeleeDamage(document.getElementById("build-melee-stats"), document.getElementById("build-melee-statsAvg"), meleeStats);
let defenseStats = player_build.getDefenseStats();
displayDefenseStats(document.getElementById("build-defense-stats"),defenseStats);
//let defenseStats = "";
@ -436,7 +436,8 @@ function calculateBuildStats() {
let spells = spell_table[player_build.weapon.get("type")];
for (let i = 0; i < 4; ++i) {
let parent_elem = document.getElementById("spell"+i+"-info");
displaySpellDamage(parent_elem, player_build, spells[i], i+1);
let overallparent_elem = document.getElementById("spell"+i+"-infoAvg");
displaySpellDamage(parent_elem, overallparent_elem, player_build, spells[i], i+1);
}
location.hash = encodeBuild();

View file

@ -97,3 +97,18 @@ Base64 = (function () {
// Base64.fromInt(-2147483648); // gives "200000"
// Base64.toInt("200000"); // gives -2147483648
/*
Turns a raw stat and a % stat into a final stat on the basis that - raw and >= 100% becomes 0 and + raw and <=-100% becomes 0.
Pct would be 0.80 for 80%, -1.20 for 120%, etc
*/
function rawToPct(raw, pct){
final = 0;
if (raw < 0){
final = (Math.min(0, raw - (raw * pct) ));
}else if(raw > 0){
final = (Math.max(0, raw + (raw * pct)));
}else{ //do nothing - final's already 0
}
return final;
}