Updating to wynn 2.0.2.3 (except item database...)

* Fix enraged blow typo; allow "or" and "and" in adv search

...forgot to update json

* Lacerate is blocked by Echo, not Mirror Image

* Misc bugfix

Fix bug with skillpoints and negative set bonus
Add final multiplier for echo

* Clean up testing folder

and add script for quick plotting pairs of ids/item values

* Fix typo in better lightweaver

add to the correct dps

* Partial update to 2.0.2.3 (festival of heroes)

patch:
- ing changes (manual)
- two endgame items (the ones that I got customs for)

bugfix:
- Fix bug in reverse mapping that mapped item "type" to "accessoryType"

* Forgot to commit all the 2.0.2.3 data files...

* Fix epilogue displayName

* Fix minor incorrectness with fromIntV invocation

don't think this was a bug? but its not the correct number of arguments lol

* Move powder ingreds to ing load sequence

not used anywhere else
also, remove extra prints in crafter

* Refactor powder special display

fix quake/chain/courage not displaying some powder special information 💀

* Finally fix satsujin to work with powder specials

thanks to powder special display refactor

* Fix mask of the awakened giving outdated stats

e

* Add prologue and gleeman's tale

wynn api when
...fix epilgoue

---------

Co-authored-by: hppeng <hppeng>
This commit is contained in:
hppeng-wynn 2023-04-14 17:18:52 -07:00 committed by GitHub
parent 4b9460ebc2
commit d9e5d6da95
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 27503 additions and 26243 deletions

52140
clean.json

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
data/2.0.2.3/atree.json Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
data/2.0.2.3/items.json Normal file

File diff suppressed because one or more lines are too long

1069
data/2.0.2.3/tomes.json Normal file

File diff suppressed because it is too large Load diff

View file

@ -15710,8 +15710,8 @@
"maximum": 7
},
"lq": {
"minimum": 3,
"maximum": 4
"minimum": 4,
"maximum": 5
}
},
"itemIDs": {
@ -16169,7 +16169,7 @@
"lvl": 4,
"ids": {},
"itemIDs": {
"dura": -70,
"dura": -30,
"strReq": 0,
"dexReq": 0,
"intReq": 0,
@ -18451,7 +18451,7 @@
"posMods": {
"left": 0,
"right": 0,
"above": 30,
"above": 25,
"under": 0,
"touching": 0,
"notTouching": 0
@ -23923,7 +23923,7 @@
"lvl": 100,
"ids": {},
"itemIDs": {
"dura": -175,
"dura": -160,
"strReq": 0,
"dexReq": 0,
"intReq": 0,
@ -23934,7 +23934,7 @@
},
"consumableIDs": {
"charges": 0,
"dura": -300
"dura": -275
},
"posMods": {
"left": 110,
@ -24214,8 +24214,8 @@
"right": 0,
"above": 0,
"under": 0,
"touching": 12,
"notTouching": 12
"touching": 15,
"notTouching": 15
},
"id": 569
},
@ -24722,7 +24722,7 @@
"above": 0,
"under": 0,
"touching": -100,
"notTouching": 75
"notTouching": 55
},
"id": 582
},
@ -24913,8 +24913,8 @@
"posMods": {
"left": -100,
"right": -100,
"above": 75,
"under": 75,
"above": 60,
"under": 60,
"touching": 0,
"notTouching": 0
},
@ -24942,10 +24942,10 @@
"dura": -300
},
"posMods": {
"left": -75,
"right": -75,
"above": -50,
"under": -50,
"left": -50,
"right": -50,
"above": -75,
"under": -75,
"touching": 120,
"notTouching": 0
},
@ -25983,7 +25983,7 @@
"above": 0,
"under": 0,
"touching": 0,
"notTouching": 65
"notTouching": 55
},
"id": 610
},
@ -26140,8 +26140,8 @@
"right": 0,
"above": 0,
"under": 0,
"touching": 33,
"notTouching": 33
"touching": 25,
"notTouching": 25
},
"id": 614
},
@ -26688,8 +26688,8 @@
"right": -122,
"above": 0,
"under": 0,
"touching": -29,
"notTouching": -29
"touching": -15,
"notTouching": -15
},
"id": 627
},
@ -26846,7 +26846,7 @@
"posMods": {
"left": 0,
"right": 0,
"above": -180,
"above": -140,
"under": 0,
"touching": 0,
"notTouching": 0
@ -27433,12 +27433,12 @@
"dura": 0
},
"posMods": {
"left": -45,
"right": -45,
"left": -30,
"right": -30,
"above": 0,
"under": 0,
"touching": 45,
"notTouching": 45
"touching": 30,
"notTouching": 30
},
"id": 645
},
@ -28657,8 +28657,8 @@
"right": 0,
"above": 0,
"under": 0,
"touching": 20,
"notTouching": 20
"touching": 15,
"notTouching": 15
},
"id": 691
},
@ -28715,7 +28715,7 @@
"lvl": 80,
"ids": {},
"itemIDs": {
"dura": -150,
"dura": -140,
"strReq": -25,
"dexReq": -25,
"intReq": -25,
@ -28802,8 +28802,8 @@
"right": 0,
"above": 0,
"under": 0,
"touching": 40,
"notTouching": 80
"touching": 25,
"notTouching": 45
},
"id": 674
},
@ -29051,4 +29051,4 @@
},
"id": 679
}
]
]

File diff suppressed because one or more lines are too long

View file

@ -94,10 +94,12 @@ let str_item_fields = [ "name", "displayName", "lore", "color", "tier", "set", "
//File reading for ID translations for JSON purposes
let reversetranslations = new Map();
let translations = new Map([["name", "name"],["displayName", "displayName"],["tier", "tier"],["set", "set"],["sockets", "slots"],["type", "type"],["armorColor", "color"],["addedLore", "lore"],["dropType", "drop"],["quest", "quest"],["restrictions", "restrict"],["damage", "nDam"],["fireDamage", "fDam"],["waterDamage", "wDam"],["airDamage", "aDam"],["thunderDamage", "tDam"],["earthDamage", "eDam"],["attackSpeed", "atkSpd"],["health", "hp"],["fireDefense", "fDef"],["waterDefense", "wDef"],["airDefense", "aDef"],["thunderDefense", "tDef"],["earthDefense", "eDef"],["level", "lvl"],["classRequirement", "classReq"],["strength", "strReq"],["dexterity", "dexReq"],["intelligence", "intReq"],["agility", "agiReq"],["defense", "defReq"],["healthRegen", "hprPct"],["manaRegen", "mr"],["spellDamageBonus", "sdPct"],["spellElementalDamageBonus", "rSdPct"],["spellNeutralDamageBonus", "nSdPct"],["spellFireDamageBonus", "fSdPct"],["spellWaterDamageBonus", "wSdPct"],["spellAirDamageBonus", "aSdPct"],["spellThunderDamageBonus", "tSdPct"],["spellEarthDamageBonus", "eSdPct"],["mainAttackDamageBonus", "mdPct"],["mainAttackElementalDamageBonus", "rMdPct"],["mainAttackNeutralDamageBonus", "nMdPct"],["mainAttackFireDamageBonus", "fMdPct"],["mainAttackWaterDamageBonus", "wMdPct"],["mainAttackAirDamageBonus", "aMdPct"],["mainAttackThunderDamageBonus", "tMdPct"],["mainAttackEarthDamageBonus", "eMdPct"],["lifeSteal", "ls"],["manaSteal", "ms"],["xpBonus", "xpb"],["lootBonus", "lb"],["reflection", "ref"],["strengthPoints", "str"],["dexterityPoints", "dex"],["intelligencePoints", "int"],["agilityPoints", "agi"],["defensePoints", "def"],["thorns", "thorns"],["exploding", "expd"],["speed", "spd"],["attackSpeedBonus", "atkTier"],["poison", "poison"],["healthBonus", "hpBonus"],["soulPoints", "spRegen"],["emeraldStealing", "eSteal"],["healthRegenRaw", "hprRaw"],["spellDamageBonusRaw", "sdRaw"],["spellElementalDamageBonusRaw", "rSdRaw"],["spellNeutralDamageBonusRaw", "nSdRaw"],["spellFireDamageBonusRaw", "fSdRaw"],["spellWaterDamageBonusRaw", "wSdRaw"],["spellAirDamageBonusRaw", "aSdRaw"],["spellThunderDamageBonusRaw", "tSdRaw"],["spellEarthDamageBonusRaw", "eSdRaw"],["mainAttackDamageBonusRaw", "mdRaw"],["mainAttackElementalDamageBonusRaw", "rMdRaw"],["mainAttackNeutralDamageBonusRaw", "nMdRaw"],["mainAttackFireDamageBonusRaw", "fMdRaw"],["mainAttackWaterDamageBonusRaw", "wMdRaw"],["mainAttackAirDamageBonusRaw", "aMdRaw"],["mainAttackThunderDamageBonusRaw", "tMdRaw"],["mainAttackEarthDamageBonusRaw", "eMdRaw"],["fireDamageBonus", "fDamPct"],["waterDamageBonus", "wDamPct"],["airDamageBonus", "aDamPct"],["thunderDamageBonus", "tDamPct"],["earthDamageBonus", "eDamPct"],["bonusFireDefense", "fDefPct"],["bonusWaterDefense", "wDefPct"],["bonusAirDefense", "aDefPct"],["bonusThunderDefense", "tDefPct"],["bonusEarthDefense", "eDefPct"],["accessoryType", "type"],["identified", "fixID"],["skin", "skin"],["category", "category"],["spellCostPct1", "spPct1"],["spellCostRaw1", "spRaw1"],["spellCostPct2", "spPct2"],["spellCostRaw2", "spRaw2"],["spellCostPct3", "spPct3"],["spellCostRaw3", "spRaw3"],["spellCostPct4", "spPct4"],["spellCostRaw4", "spRaw4"],["sprint", "sprint"],["sprintRegen", "sprintReg"],["jumpHeight", "jh"],["lootQuality", "lq"],["gatherXpBonus", "gXp"],["gatherSpeed", "gSpd"]]);
let _translations_list = [["name", "name"],["displayName", "displayName"],["tier", "tier"],["set", "set"],["sockets", "slots"],["type", "type"],["armorColor", "color"],["addedLore", "lore"],["dropType", "drop"],["quest", "quest"],["restrictions", "restrict"],["damage", "nDam"],["fireDamage", "fDam"],["waterDamage", "wDam"],["airDamage", "aDam"],["thunderDamage", "tDam"],["earthDamage", "eDam"],["attackSpeed", "atkSpd"],["health", "hp"],["fireDefense", "fDef"],["waterDefense", "wDef"],["airDefense", "aDef"],["thunderDefense", "tDef"],["earthDefense", "eDef"],["level", "lvl"],["classRequirement", "classReq"],["strength", "strReq"],["dexterity", "dexReq"],["intelligence", "intReq"],["agility", "agiReq"],["defense", "defReq"],["healthRegen", "hprPct"],["manaRegen", "mr"],["spellDamageBonus", "sdPct"],["spellElementalDamageBonus", "rSdPct"],["spellNeutralDamageBonus", "nSdPct"],["spellFireDamageBonus", "fSdPct"],["spellWaterDamageBonus", "wSdPct"],["spellAirDamageBonus", "aSdPct"],["spellThunderDamageBonus", "tSdPct"],["spellEarthDamageBonus", "eSdPct"],["mainAttackDamageBonus", "mdPct"],["mainAttackElementalDamageBonus", "rMdPct"],["mainAttackNeutralDamageBonus", "nMdPct"],["mainAttackFireDamageBonus", "fMdPct"],["mainAttackWaterDamageBonus", "wMdPct"],["mainAttackAirDamageBonus", "aMdPct"],["mainAttackThunderDamageBonus", "tMdPct"],["mainAttackEarthDamageBonus", "eMdPct"],["lifeSteal", "ls"],["manaSteal", "ms"],["xpBonus", "xpb"],["lootBonus", "lb"],["reflection", "ref"],["strengthPoints", "str"],["dexterityPoints", "dex"],["intelligencePoints", "int"],["agilityPoints", "agi"],["defensePoints", "def"],["thorns", "thorns"],["exploding", "expd"],["speed", "spd"],["attackSpeedBonus", "atkTier"],["poison", "poison"],["healthBonus", "hpBonus"],["soulPoints", "spRegen"],["emeraldStealing", "eSteal"],["healthRegenRaw", "hprRaw"],["spellDamageBonusRaw", "sdRaw"],["spellElementalDamageBonusRaw", "rSdRaw"],["spellNeutralDamageBonusRaw", "nSdRaw"],["spellFireDamageBonusRaw", "fSdRaw"],["spellWaterDamageBonusRaw", "wSdRaw"],["spellAirDamageBonusRaw", "aSdRaw"],["spellThunderDamageBonusRaw", "tSdRaw"],["spellEarthDamageBonusRaw", "eSdRaw"],["mainAttackDamageBonusRaw", "mdRaw"],["mainAttackElementalDamageBonusRaw", "rMdRaw"],["mainAttackNeutralDamageBonusRaw", "nMdRaw"],["mainAttackFireDamageBonusRaw", "fMdRaw"],["mainAttackWaterDamageBonusRaw", "wMdRaw"],["mainAttackAirDamageBonusRaw", "aMdRaw"],["mainAttackThunderDamageBonusRaw", "tMdRaw"],["mainAttackEarthDamageBonusRaw", "eMdRaw"],["fireDamageBonus", "fDamPct"],["waterDamageBonus", "wDamPct"],["airDamageBonus", "aDamPct"],["thunderDamageBonus", "tDamPct"],["earthDamageBonus", "eDamPct"],["bonusFireDefense", "fDefPct"],["bonusWaterDefense", "wDefPct"],["bonusAirDefense", "aDefPct"],["bonusThunderDefense", "tDefPct"],["bonusEarthDefense", "eDefPct"],["accessoryType", "type"],["identified", "fixID"],["skin", "skin"],["category", "category"],["spellCostPct1", "spPct1"],["spellCostRaw1", "spRaw1"],["spellCostPct2", "spPct2"],["spellCostRaw2", "spRaw2"],["spellCostPct3", "spPct3"],["spellCostRaw3", "spRaw3"],["spellCostPct4", "spPct4"],["spellCostRaw4", "spRaw4"],["sprint", "sprint"],["sprintRegen", "sprintReg"],["jumpHeight", "jh"],["lootQuality", "lq"],["gatherXpBonus", "gXp"],["gatherSpeed", "gSpd"]];
let translations = new Map(_translations_list);
//does not include damMobs (wep tomes) and defMobs (armor tomes)
for (const [k, v] of translations) {
for (const [k, v] of _translations_list) {
if (reversetranslations.has(v)) { continue; }
reversetranslations.set(v, k);
}

View file

@ -3665,7 +3665,7 @@ const atrees = {
},
{
"display_name": "Enraged Blow",
"desc": "While Corrupted, every 1% of Health you lose will increase your damage by +2% (Max 80%)",
"desc": "While Corrupted, every 1% of Health you lose will increase your damage by +1.5% (Max 80%)",
"archetype": "Fallen",
"archetype_req": 0,
"base_abil": "Bak'al's Grasp",
@ -6964,7 +6964,7 @@ const atrees = {
"effects": [
{
"type": "add_spell_prop",
"target_part": "Orb Damage",
"target_part": "Orb DPS",
"base_spell": 5,
"hits": {
"Single Orb": 1
@ -7906,7 +7906,6 @@ const atrees = {
"desc": "After leaving Vanish, summon 3 Clones that will follow you and protect you (15s Cooldown). When hit, gain a chance to take 80% less damage and lose 1 Clone.",
"archetype": "Trickster",
"archetype_req": 2,
"base_abil": "Dash",
"parents": [
"Sticky Bomb"
],
@ -7943,7 +7942,7 @@ const atrees = {
],
"dependencies": [],
"blockers": [
"Mirror Image"
"Echo"
],
"cost": 2,
"display": {
@ -8545,7 +8544,7 @@ const atrees = {
"desc": "Your Clones will mimic your spells and abilities. While they are active, deal -60% damage.",
"archetype": "Trickster",
"archetype_req": 6,
"base_abil": "Dash",
"base_abil": "Mirror Image",
"parents": [
"Sandbagging",
"Shurikens"
@ -8581,6 +8580,22 @@ const atrees = {
"behavior": "modify",
"target_part": "Slash Damage",
"multipliers": [ 690, 0, 0, 110, 0, 0 ]
},
{
"type": "stat_scaling",
"slider": true,
"slider_name": "Spell Copies",
"slider_step": 1,
"slider_max": 3,
"output": [
{
"type": "stat",
"name": "damMult.EchoCast"
}
],
"scaling": [
100
]
}
]
},
@ -9123,7 +9138,7 @@ const atrees = {
"desc": "Improve your damage while your Clones are active by +15%",
"archetype": "Trickster",
"archetype_req": 7,
"base_abil": "Dash",
"base_abil": "Mirror Image",
"parents": [
"Cheaper Smoke Bomb 2",
"Blade Fury"
@ -9173,7 +9188,7 @@ const atrees = {
},
{
"display_name": "Satsujin",
"desc": "If an enemy has 3 Marks and 70% of their health or more, your next Multihit or Main Attack will deal triple damage. (20s Cooldown, per enemy)",
"desc": "If an enemy has 3 Marks, your next Multihit or Damaging Powder Special will deal double damage. (20s Cooldown, per enemy)",
"archetype": "Shadestepper",
"archetype_req": 13,
"parents": [
@ -9210,7 +9225,7 @@ const atrees = {
},
{
"type": "stat",
"name": "damMult.Satsujin:0.Melee",
"name": "damMult.Satsujin:0.Powder Special",
"value": 100
}
]
@ -9222,7 +9237,7 @@ const atrees = {
"desc": "Summon +3 additional Clones. (+15s Cooldown)",
"archetype": "Trickster",
"archetype_req": 8,
"base_abil": "Dash",
"base_abil": "Mirror Image",
"parents": [
"Cheaper Smoke Bomb 2"
],
@ -9237,7 +9252,14 @@ const atrees = {
"icon": "node_2"
},
"properties": {},
"effects": []
"effects": [
{
"type": "stat_scaling",
"slider": true,
"slider_name": "Spell Copies",
"slider_max": 3
}
]
},
{
"display_name": "Diversion",
@ -11564,17 +11586,17 @@ const atrees = {
{
"type": "stat",
"name": "damMult.Mask",
"value": 30
"value": 35
},
{
"type": "stat",
"name": "defMult.Mask",
"value": 30
"value": 35
},
{
"type": "stat",
"name": "spd",
"value": 25
"value": 80
},
{
"type": "stat",

File diff suppressed because one or more lines are too long

View file

@ -30,7 +30,8 @@ let atree_data = null;
const wynn_version_names = [
'2.0.1.1',
'2.0.1.2',
'2.0.2.1'
'2.0.2.1',
'2.0.2.3'
];
const WYNN_VERSION_LATEST = wynn_version_names.length - 1;
// Default to the newest version.

View file

@ -117,7 +117,7 @@ class PowderSpecialDisplayNode extends ComputeNode {
const powder_specials = input_map.get('powder-specials');
const stats = input_map.get('stats');
const weapon = input_map.get('build').weapon;
displayPowderSpecials(document.getElementById("powder-special-stats"), powder_specials, stats, weapon.statMap, true);
displayPowderSpecials(document.getElementById("powder-special-stats"), powder_specials, stats, weapon.statMap);
}
}

View file

@ -136,8 +136,6 @@ function calculateCraft() {
}
let ingreds = [];
for (i = 1; i < 7; i++) {
console.log("ing-choice-"+i);
// console.log(getValue("ing-choice-"+i));
getValue("ing-choice-" + i) === "" ? ingreds.push(expandIngredient(ingMap.get("No Ingredient"))) : ingreds.push(expandIngredient(ingMap.get(getValue("ing-choice-" + i))));
}
let atkSpd = "NORMAL"; //default attack speed will be normal.

View file

@ -246,7 +246,6 @@ class Custom {
}
}
let type = this.statMap.get("type").toLowerCase();
console.log(type);
if (weaponTypes.includes(type)) {
for (const n of ["nDam", "eDam", "tDam", "wDam", "fDam", "aDam"]) {
if (!(this.statMap.has(n) && this.statMap.get(n))) {

View file

@ -340,17 +340,3 @@ const default_spells = {
]
}]
};
const spell_table = {
"powder": [ //This is how instant-damage powder specials are implemented.
{ title: "Quake", cost: 0, parts:[
{ subtitle: "Total Damage", type: "damage", multiplier: [155, 220, 285, 350, 415], conversion: [0,100,0,0,0,0], summary: true},
] },
{ title: "Chain Lightning", cost: 0, parts: [
{ subtitle: "Total Damage", type: "damage", multiplier: [200, 225, 250, 275, 300], conversion: [0,0,100,0,0,0], summary: true},
]},
{ title: "Courage", cost: 0, parts: [
{ subtitle: "Total Damage", type: "damage", multiplier: [75, 87.5, 100, 112.5, 125], conversion: [0,0,0,0,100,0], summary: true},
]}, //[75, 87.5, 100, 112.5, 125]
]
};

View file

@ -1231,7 +1231,7 @@ function displayDefenseStats(parent_elem, statMap, insertSummary){
}
}
function displayPowderSpecials(parent_elem, powderSpecials, stats, weapon, overall=false) {
function displayPowderSpecials(parent_elem, powderSpecials, stats, weapon) {
parent_elem.textContent = "";
if (powderSpecials.length === 0) {
parent_elem.style = "display: none";
@ -1252,129 +1252,66 @@ function displayPowderSpecials(parent_elem, powderSpecials, stats, weapon, overa
//each entry of powderSpecials is [ps, power]
for (special of specials) {
//iterate through the special and display its effects.
let powder_special = make_elem("p", ["pt-3"]);
let powder_special_elem = make_elem("p", ["pt-3"]);
let specialSuffixes = new Map([ ["Duration", " sec"], ["Radius", " blocks"], ["Chains", ""], ["Damage", "%"], ["Damage Boost", "%"], ["Knockback", " blocks"] ]);
let specialTitle = make_elem("p");
let specialEffects = make_elem("p");
specialTitle.classList.add(damageClasses[powderSpecialStats.indexOf(special[0]) + 1]);
let effects = special[0]["weaponSpecialEffects"];
// TODO janky and depends on the order of powder specials being ETWFA. This should be encoded in the powder special object.
let element_num = powderSpecialStats.indexOf(special[0]) + 1;
specialTitle.classList.add(damageClasses[element_num]);
let powder_special = special[0];
let power = special[1];
specialTitle.textContent = special[0]["weaponSpecialName"] + " " + Math.floor((power-1)*0.5 + 4) + (power % 2 == 0 ? ".5" : "");
specialTitle.textContent = powder_special.weaponSpecialName + " " + Math.floor((power-1)*0.5 + 4) + (power % 2 == 0 ? ".5" : "");
if (!overall || powderSpecialStats.indexOf(special[0]) == 2 || powderSpecialStats.indexOf(special[0]) == 3 || powderSpecialStats.indexOf(special[0]) == 4) {
for (const [key,value] of effects) {
for (const [key,value] of powder_special.weaponSpecialEffects) {
if(key === "Damage"){
//if this special is an instant-damage special (Quake, Chain Lightning, Courage Burst), display the damage.
let specialDamage = document.createElement("p");
// specialDamage.classList.add("item-margin");
let conversions = [0, 0, 0, 0, 0, 0];
conversions[element_num] = powder_special.weaponSpecialEffects.get("Damage")[power-1];
let _results = calculateSpellDamage(stats, weapon, conversions, false, true, "0.Powder Special");
let critChance = skillPointsToPercentage(skillpoints[1]);
let save_damages = [];
let totalDamNormal = _results[0];
let totalDamCrit = _results[1];
let results = _results[2];
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;
let averageWrap = document.createElement("p");
let averageLabel = document.createElement("span");
averageLabel.textContent = "Average: ";
let averageLabelDmg = document.createElement("span");
averageLabelDmg.classList.add("Damage");
averageLabelDmg.textContent = averageDamage.toFixed(2);
averageWrap.appendChild(averageLabel);
averageWrap.appendChild(averageLabelDmg);
specialDamage.appendChild(averageWrap);
specialEffects.append(specialDamage);
}
else {
let effect = document.createElement("p");
effect.textContent += key + ": " + value[power-1] + specialSuffixes.get(key);
if(key === "Damage"){
effect.textContent += elementIcons[powderSpecialStats.indexOf(special[0])];
}
if(special[0]["weaponSpecialName"] === "Wind Prison" && key === "Damage Boost") {
effect.textContent += " (only 1st hit)";
}
specialEffects.appendChild(effect);
}
}
powder_special.appendChild(specialTitle);
powder_special.appendChild(specialEffects);
//if this special is an instant-damage special (Quake, Chain Lightning, Courage Burst), display the damage.
let specialDamage = document.createElement("p");
// specialDamage.classList.add("item-margin");
let spells = spell_table["powder"];
if (powderSpecialStats.indexOf(special[0]) == 0 || powderSpecialStats.indexOf(special[0]) == 1 || powderSpecialStats.indexOf(special[0]) == 3) { //Quake, Chain Lightning, or Courage
let spell = (powderSpecialStats.indexOf(special[0]) == 3 ? spells[2] : spells[powderSpecialStats.indexOf(special[0])]);
let part = spell["parts"][0];
powder_special_elem.appendChild(specialTitle);
powder_special_elem.appendChild(specialEffects);
let tmp_conv = [];
for (let i in part.conversion) {
tmp_conv.push(part.conversion[i] * part.multiplier[power-1] / 100);
}
console.log(tmp_conv);
let _results = calculateSpellDamage(stats, weapon, tmp_conv, false, true);
let critChance = skillPointsToPercentage(skillpoints[1]);
let save_damages = [];
let totalDamNormal = _results[0];
let totalDamCrit = _results[1];
let results = _results[2];
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;
let averageWrap = document.createElement("p");
let averageLabel = document.createElement("span");
averageLabel.textContent = "Average: ";
let averageLabelDmg = document.createElement("span");
averageLabelDmg.classList.add("Damage");
averageLabelDmg.textContent = averageDamage.toFixed(2);
averageWrap.appendChild(averageLabel);
averageWrap.appendChild(averageLabelDmg);
specialDamage.appendChild(averageWrap);
if (!overall) {
let nonCritLabel = document.createElement("p");
nonCritLabel.textContent = "Non-Crit Average: "+nonCritAverage.toFixed(2);
nonCritLabel.classList.add("damageSubtitle");
nonCritLabel.classList.add("item-margin");
specialDamage.append(nonCritLabel);
for (let i = 0; i < 6; i++){
if (results[i][1] > 0){
let p = document.createElement("p");
p.classList.add("damagep");
p.classList.add(damageClasses[i]);
p.textContent = results[i][0]+"-"+results[i][1];
specialDamage.append(p);
}
}
let normalDamage = document.createElement("p");
normalDamage.textContent = "Total: " + totalDamNormal[0].toFixed(2) + "-" + totalDamNormal[1].toFixed(2);
normalDamage.classList.add("itemp");
specialDamage.append(normalDamage);
let nonCritChanceLabel = document.createElement("p");
nonCritChanceLabel.textContent = "Non-Crit Chance: " + ((1-critChance)*100).toFixed(2) + "%";
specialDamage.append(nonCritChanceLabel);
let critLabel = document.createElement("p");
critLabel.textContent = "Crit Average: "+critAverage.toFixed(2);
critLabel.classList.add("damageSubtitle");
critLabel.classList.add("item-margin");
specialDamage.append(critLabel);
for (let i = 0; i < 6; i++){
if (results[i][1] > 0){
let p = document.createElement("p");
p.classList.add("damagep");
p.classList.add(damageClasses[i]);
p.textContent = results[i][2]+"-"+results[i][3];
specialDamage.append(p);
}
}
let critDamage = document.createElement("p");
critDamage.textContent = "Total: " + totalDamCrit[0].toFixed(2) + "-" + totalDamCrit[1].toFixed(2);
critDamage.classList.add("itemp");
specialDamage.append(critDamage);
let critChanceLabel = document.createElement("p");
critChanceLabel.textContent = "Crit Chance: " + (critChance*100).toFixed(2) + "%";
specialDamage.append(critChanceLabel);
save_damages.push(averageDamage);
}
powder_special.append(specialDamage);
}
parent_elem.appendChild(powder_special);
parent_elem.appendChild(powder_special_elem);
}
}

View file

@ -50,6 +50,14 @@ const ExprParser = (function() {
case '=':
pushSymbol(exprStr[col]);
continue;
case 'or':
tokens.push({ type: '|' });
col += 2;
continue;
case 'and':
tokens.push({ type: '&' });
col += 2;
continue;
case '>':
pushSymbol(exprStr[col + 1] === '=' ? '>=' : '>');
continue;

View file

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

View file

@ -1,4 +1,4 @@
const ING_DB_VERSION = 20;
const ING_DB_VERSION = 21;
// @See https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/video-store/index.js
@ -201,39 +201,29 @@ function init_ing_maps() {
ingList.push(ing.displayName);
ingIDMap.set(ing.id, ing.displayName);
let numerals = new Map([[1, "I"], [2, "II"], [3, "III"], [4, "IV"], [5, "V"], [6, "VI"]]);
// pairs of (dura, req)
let powder_ing_info = [
[-35,0],[-52.5,0],[-70,10],[-91,20],[-112,28],[-133,36]
];
for (let i = 0; i < 5; i ++) {
for (const powderIng of powderIngreds) {
for (let powder_tier = 0; powder_tier < 6; ++powder_tier) {
powder_info = powder_ing_info[powder_tier];
let ing = {
name: "" + damageClasses[i+1] + " Powder " + numerals.get(powderIngreds.indexOf(powderIng) + 1),
name: "" + damageClasses[i+1] + " Powder " + numerals.get(powder_tier + 1),
tier: 0,
lvl: 0,
skills: ["ARMOURING", "TAILORING", "WEAPONSMITHING", "WOODWORKING", "JEWELING"],
ids: {},
isPowder: true,
pid: 6*i + powderIngreds.indexOf(powderIng),
itemIDs: {"dura": powderIng["durability"], "strReq": 0, "dexReq": 0,"intReq": 0,"defReq": 0,"agiReq": 0},
pid: 6*i + powder_tier,
itemIDs: {"dura": powder_info[0], "strReq": 0, "dexReq": 0,"intReq": 0,"defReq": 0,"agiReq": 0},
consumableIDs: {"dura": 0, "charges": 0},
posMods: {"left": 0, "right": 0, "above": 0, "under": 0, "touching": 0, "notTouching": 0}
};
ing.id = 4001 + ing.pid;
ing.displayName = ing.name;
switch(i) {
case 0:
ing.itemIDs["strReq"] = powderIng["skpReq"];
break;
case 1:
ing.itemIDs["dexReq"] = powderIng["skpReq"];
break;
case 2:
ing.itemIDs["intReq"] = powderIng["skpReq"];
break;
case 3:
ing.itemIDs["defReq"] = powderIng["skpReq"];
break;
case 4:
ing.itemIDs["agiReq"] = powderIng["skpReq"];
break;
}
ing.itemIDs[skp_order[i] + "Req"] = powder_info[1];
ingMap.set(ing.displayName, ing);
ingList.push(ing.displayName);
ingIDMap.set(ing.id, ing.displayName);

View file

@ -31,17 +31,6 @@ let powderStats = [
_p(2,6,11,3,1), _p(3,10,14,6,2), _p(4,11,17,10,3), _p(5,11,22,16,5), _p(7,12,28,24,9), _p(8,14,35,34,13)
];
class PowderIngredient {
constructor(durability, skpReq) {
this.durability = durability;
this.skpReq = skpReq;
}
}
function _pi(a,b) { return new PowderIngredient(a,b)}
let powderIngreds = [
_pi(-35,0),_pi(-52.5,0),_pi(-70,10),_pi(-91,20),_pi(-112,28),_pi(-133,36)
];
//Ordering: [weapon special name, weapon special effects, armor special name, armor special effects]
class PowderSpecial{
constructor(wSpName, wSpEff, aSpName, aSpEff, cap){

View file

@ -240,15 +240,31 @@ function construct_scc_graph(items_to_consider) {
parents: [],
};
for (const item of items_to_consider) {
nodes.push({item: item, children: [terminal_node], parents: [root_node]});
const set_neg = [false, false, false, false, false];
const set_pos = [false, false, false, false, false];
const set_name = item.set;
if (set_name) {
const bonuses = sets.get(set_name).bonuses;
for (const bonus of bonuses) {
for (const i in skp_order) {
if (bonus[skp_order[i]] > 0) { set_pos[i] = true; }
if (bonus[skp_order[i]] < 0) { set_neg[i] = true; }
}
}
}
nodes.push({item: item, children: [terminal_node], parents: [root_node], set_pos: set_pos, set_neg: set_neg});
}
// Dependency graph construction.
for (const node_a of nodes) {
const {item: a, children: a_children} = node_a;
const {item: a, children: a_children, set_pos: a_set_pos} = node_a;
for (const node_b of nodes) {
const {item: b, parents: b_parents} = node_b;
const {item: b, parents: b_parents, set_neg: b_set_neg} = node_b;
const setName = b.set;
for (let i = 0; i < 5; ++i) {
if (a.skillpoints[i] > 0 && (a.reqs[i] < b.reqs[i] || b.skillpoints[i] < 0)) {
if ((a.skillpoints[i] > 0 || a_set_pos[i] > 0)
&& (a.reqs[i] < b.reqs[i] || b.skillpoints[i] < 0 || b_set_neg[i] < 0)) {
a_children.push(node_b);
b_parents.push(node_a);
break;
@ -259,3 +275,4 @@ function construct_scc_graph(items_to_consider) {
const sccs = make_SCC_graph(root_node, nodes);
return [root_node, terminal_node, sccs];
}

View file

@ -278,7 +278,7 @@ Base64 = (function () {
let b64_str = "";
let i = 0;
while (i < this.length) {
b64_str += Base64.fromIntV(this.slice(i, i + 6), 1);
b64_str += Base64.fromIntN(this.slice(i, i + 6), 1);
i += 6;
}
@ -997,4 +997,4 @@ if (screen.width < 992) {
}
scrollPos = document.documentElement.scrollTop;
});
}
}

View file

@ -3798,5 +3798,9 @@
"Athanasia": 3796,
"Provenance": 3797,
"Veneration": 3798,
"Reckoning": 3799
"Reckoning": 3799,
"Eleventh Hour": 3800,
"Epilogue": 3801,
"Prologue": 3802,
"Gleeman's Tale": 3803
}

View file

@ -0,0 +1,137 @@
import json
import numpy as np
import matplotlib.pyplot as plt
def max_id(item, id_name, invert=False):
"""
Calculate the "max roll" for a given ID.
Parameters
Name type desc
----------------------------------------------------------
item json Item json data
id_name string name of the ID to get
invert bool Whether to "invert" (raw cost and %cost have funny
0.7-1.3 positive roll and 0.3-1.3 negative roll)
Return:
val: float -- max roll id value.
"""
id_val = item.get(id_name, 0)
if id_val == 0: # if the ID isn't present, its just going to be zero
return 0
if item.get('fixID', False):
# If the item is a fixed roll item, don't roll the ID.
return id_val
# roll the ID. Negative roll (and invert) max roll is 0.7; positive max is 1.3.
if bool(id_val < 0) != bool(invert): # logical XOR
val = round(id_val * 0.7)
else: #if bool(id_val > 0) != bool(invert):
val = round(id_val * 1.3)
if val == 0: # if we rounded to zero, then restore the id as sign(base_val).
val = id_val / abs(id_val)
return val
def mv(item, base_costs):
"""
Compute mana value for an item.
Takes a maximum mana value
- assuming 1 melee value (3/3 mana steal = 1 mana value)
- assuming spells 1, 3, and 4 are cycle spells.
Ignores spell 2 for spell cost purposes.
Parameters
Name type desc
----------------------------------------------------------
item json Item json data
base_costs list[float] base spell cost [spell1, spell2, spell3, spell4]
Return:
val: float -- mana value.
"""
cost_reductions = sorted([
max_id(item, 'spRaw1', True) + base_costs[0]*max_id(item, 'spPct1', True)/100,
#max_id(item, 'spRaw2', True) + base_costs[1]*max_id(item, 'spPct2', True)/100,
max_id(item, 'spRaw3', True) + base_costs[2]*max_id(item, 'spPct3', True)/100,
max_id(item, 'spRaw4', True) + base_costs[3]*max_id(item, 'spPct4', True)/100,
])
cost_mv = -sum(cost_reductions[:2])
return (
max_id(item, 'ms')/3
+ max_id(item, 'mr')/5
+ cost_mv
)
###########################
# constants for damage calc.
elements = 'rnetwfa'
raw_ids = ['sdRaw'] + [x+'SdRaw' for x in elements] + [x+'DamRaw' for x in elements]
# these %boosts apply to all damages.
percent_all_ids = ['sdPct', 'rSdPct']
# this one is a list of lists.
# the mini lists are sub-sums, the big list gets max'd over (elemental damage works like this.)
percent_max_id_groups = list(zip([x+'DamPct' for x in 'etwfa'] + [x+'SdPct' for x in 'etwfa'])) # exclude neutral lel
###########################
def damage(item, weapon_base):
"""
Compute effective damage bonus.
Note that this assumes the weapon aligns with whatever bonus this item is giving.
Parameters
Name type desc
----------------------------------------------------------
item json Item json data
weapon_base float weapon base dps
Return:
val: float -- raw damage bonus given (approximate) for the weapon.
"""
total = sum(max_id(item, x) for x in raw_ids)
total += weapon_base * sum(max_id(item, x) for x in percent_all_ids) / 100
total += weapon_base * max(sum(max_id(item, y) for y in x) for x in percent_max_id_groups) / 100
return total
#################################
# NOTE: Edit these parameters! LOL i was lazy to make a CLI
level_threshold = 80
weapon_base = 700
base_costs = [35, 20, 35, 35]
item_type = 'leggings'
# TODO: Changeme to point to a copy of wynnbuilder's compress.json file!
items = json.load(open("../../compress.json"))['items']
#################################
# collect data from items.
points = []
names = dict()
for item in items:
if item['type'] == item_type and item['lvl'] > level_threshold:
# Edit me to see other comparisons!
#point = (mv(item, base_costs), damage(item, weapon_base))
point = (max_id(item, 'spd'), item.get('hp', 0) + max_id(item, 'hpBonus'))
points.append(point)
# just some shenanigans to aggregate text that happens to fall on the same point.
if point in names:
names[point] += '\n'+item.get('displayName', item['name'])
else:
names[point] = item.get('displayName', item['name'])
points = np.array(points)
# plot points.
plt.figure()
plt.scatter(points[:, 0], points[:, 1])
# and add annotations.
for point, txt in names.items():
plt.annotate(txt, point)
plt.show()