Compare commits

...

15 commits
dev ... docs

Author SHA1 Message Date
ferricles 68dda3a649 minor fixes + rewording 2024-06-10 01:09:52 -04:00
ferricles 73a456ddf5 wynnfo overhaul, move advanced item search help to wynnfo, new powder mechanics wynnfo post, add damage type tags to wynnstyles.css 2024-06-09 23:26:06 -04:00
ferricles 11842d4fce add clickable post thumbnails to docs homepage 2024-06-05 02:41:43 -04:00
ferricles 0a6a6aea8b make article sections wider, fix typo in items adv help page, docs homepage 2024-06-04 02:43:32 -04:00
ferricles 785400de41 docs folder for all documentation pages, moved advanced item search help page to docs 2024-06-04 02:27:10 -04:00
ferricles 09e4d858e6 tested katex include script 2024-06-04 02:17:27 -04:00
hppeng 4147bd813e I managed to mess up all major ids
note: major ids min file is generated along with atree. it uses numeric ids, not just json compress
2024-01-12 03:52:06 -08:00
hppeng bfe19da7c1 Fix sacshrine dependency on fluid healing
also: fix ": " in item searcher
2023-12-29 19:31:12 -08:00
hppeng 02c4ace7ae Fix missing properties in item search setup
these should be unified maybe to avoid duplicated code
2023-12-29 19:15:43 -08:00
hppeng f3b4429b2e Add missing fields to ingreds
missing ids and consumableIDs tags in some ingreds
2023-12-29 18:55:31 -08:00
hppeng-wynn df9412e994
Api v3 (#267)
* Tweak ordering to be consistent internally

* v3 items  (#266)

* item_wrapper script

for updating item data with v3 endpoint

* metadata from v3

* v3 item format

For the purpose of wynnbuilder, additional mapping might be needed.

* v3 item format

additional mapping might be needed for wb

* v3 compressed item json

* clean item json v3 format

* Update translate map to api v3

partially... we will need to redo scripts to flatmap all the items

* Fix items for 2.0.4.3

finally

* New ingredients (and parse script update)

just realized I forgot to commit the parse script this whole time

* Forgot to commit data files, and bump ing db version

* Sketchily reverse translate major ids

internalname and separate lookup table lol

* Forgot to update data files

todo: script should update all files at once

* Bump wynn version number

already outdated...

* Forgot to update 2.0.4.3 major ids

---------

Co-authored-by: hppeng <hppeng>
Co-authored-by: RawFish69 <108964215+RawFish69@users.noreply.github.com>
2023-12-29 18:33:04 -08:00
hppeng 165adf6dcc Bunch of bugfixes
- new major ID
- divine honor: reduce earth damage
- radiance: don't boost tomes, xp/loot bonuses

atree:
- parry: minor typo
- death magnet: marked dep
- nightcloak knife: 15s desc
2023-11-08 18:40:08 -08:00
hppeng 95a7160dcb Fix damage calculation for rainbow raw
wow this bug has been here for a LONG time

also bump version for ing db
2023-11-08 17:46:39 -08:00
hppeng 47368aab9f Finish updating recipes.json
why are there 4 versions of this file active at any given time
2023-11-08 17:46:30 -08:00
ShadowShift 5b623260e5 Update recipes.json (#265)
Change ratio of gems to oil as it has been updated in 2.0.4

> Updated the Jeweling Recipe Changes (Bracelet- 2:1 gems:oil, Necklaces- 3:1 gems:oil)

https://forums.wynncraft.com/threads/2-0-4-full-changelog-new-bank-lootruns-more.310535/
2023-11-08 17:46:19 -08:00
58 changed files with 323150 additions and 118176 deletions

View file

@ -82,7 +82,7 @@
<div class="container-fluid overall-box mt-lg-2" style="margin-top: 6vh;">
<!-- REMOVE THIS DIV AT SOME POINT. -->
<div class = "row scaled-font mx-auto" id = "discord-banner-dev">
<div class = "col text-center item-title">Join the <a class = "link" href = "https://discord.gg/CGavnAnerv" target = "_blank">discord</a> today to suggest new features, submit bug reports, and hangout/talk to devs!</div>
<div id='discord-banner' class = "col text-center item-title">Join the <a class = "link" href = "https://discord.gg/CGavnAnerv" target = "_blank">discord</a> today to suggest new features, submit bug reports, and hangout/talk to devs!</div>
</div>
<div class="row h-100 gx-lg-5 gy-3 mx-2 mx-lg-3 py-3 gx-0">
<div class="col-xl-6">

180859
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

@ -2,6 +2,13 @@ main {
margin: 24px 0 48px;
}
main h1 {
margin: 0 0 8px;
color: #bbb;
/* font-size: 24pt; */
font-weight: bold;
}
main h2 {
margin: 0 0 8px;
color: #bbb;
@ -18,6 +25,20 @@ main p {
font-weight: normal;
}
main span {
font-size: 13pt;
font-weight: normal;
}
main li {
margin-left: 6%;
font-size: 13pt;
font-weight: normal;
}
main ul {
font-size: 13pt;
font-weight: normal;
}
main .footer {
font-size: 10pt;
text-align: center;
@ -86,6 +107,13 @@ main .heart {
color: #e44078;
}
.fig {
margin: 16px 0 16px;
}
div.caption {
font-style: italic;
}
@keyframes scroll-bg {
0% {
background-position-x: 0;
@ -108,7 +136,7 @@ main .heart {
}
.section {
margin: 0 25vw 28px;
margin: 0 15vw 28px;
}
.docs {

27
css/docs_home.css Normal file
View file

@ -0,0 +1,27 @@
article {
background-color: #212529;
margin: 0 0 30px;
padding: 10px 10px 10px;
border-radius: 5px;
display: block;
position: relative;
box-sizing: border-box;
}
article h2 {
font-weight: bold;
}
article a {
/* display: inline-block;
width:100%;
height: 100%; */
box-sizing: border-box;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
}

View file

@ -141,43 +141,43 @@ Wynn-Related CSS
color: rgb(255, 198, 85)
}
.nDam, .Neutral {
.nDam, .Neutral, Neutral {
color: #FFAA00;
}
.nDam:before, .Neutral:before {
.nDam:before, .Neutral:before, Neutral:before {
content: "\2724" ' ';
}
.eDam, .Earth, .Earth_powder {
.eDam, .Earth, .Earth_powder, Earth {
color: #00AA00;
}
.eDam:before, .Earth:before, .Earth_powder:before { content: "\2724" ' '; }
.eDam:before, .Earth:before, .Earth_powder:before, Earth:before { content: "\2724" ' '; }
.tDam, .Thunder, .Thunder_powder {
.tDam, .Thunder, .Thunder_powder, Thunder{
color: #FFFF55;
}
.tDam:before, .Thunder:before, .Thunder_powder:before { content: "\2726" ' '; }
.tDam:before, .Thunder:before, .Thunder_powder:before, Thunder:before { content: "\2726" ' '; }
.wDam, .Water, .Water_powder {
.wDam, .Water, .Water_powder, Water {
color: #55FFFF
}
.wDam:before, .Water:before, .Water_powder:before { content: "\2749" ' '; }
.wDam:before, .Water:before, .Water_powder:before, Water:before { content: "\2749" ' '; }
.fDam, .Fire, .Fire_powder {
.fDam, .Fire, .Fire_powder, Fire {
color: #FF5555;
}
.fDam:before, .Fire:before, .Fire_powder:before { content: "\2739" ' '; }
.fDam:before, .Fire:before, .Fire_powder:before, Fire:before { content: "\2739" ' '; }
.aDam, .Air, .Air_powder {
.aDam, .Air, .Air_powder, Air {
color: #FFFFFF
}
.aDam:before, .Air:before, .Air_powder:before { content: "\274b" ' '; }
.aDam:before, .Air:before, .Air_powder:before, Air:before { content: "\274b" ' '; }
.restrict {
color: #ff8180;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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.4.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.4.3/items.json Normal file

File diff suppressed because one or more lines are too long

1
data/2.0.4.3/majid.json Executable file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1069
data/2.0.4.3/tomes.json Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -22,13 +22,13 @@
</head>
<body class = "text-light d-flex justify-content-center" id = "body">
<div id="main-sidebar" class="sidebar dark-7 dark-shadow">
<a href = "../builder/"><img src="../media/icons/new/builder.png" alt = "WynnBuilder" title = "WynnBuilder"><b>WynnBuilder</b></a>
<a href = "../crafter/"><img src = "../media/icons/new/crafter.png" alt = "WynnCrafter" title = "WynnCrafter"><b>WynnCrafter</b></a>
<a href = "../items/"><img src = "../media/icons/new/searcher.png" alt = "WynnAtlas" title = "WynnAtlas"><b>WynnAtlas</b></a>
<a href = "/customizer.html"><img src = "../media/icons/new/custom.png" alt = "WynnCustom" title = "WynnCustom"><b>WynnCustom</b></a>
<a href = "/map.html"><img src = "../media/icons/new/compass.png" alt = "WynnGPS" title = "WynnGPS"><b>WynnGPS</b></a>
<a href = "/wynnfo/index.html"><img src = "../media/icons/new/book.png" alt = "Wynnfo" title = "WynnCrafter"><b>WynnCrafter</b></a>
<a onclick = "toggleIcons()"><img src = "../media/icons/new/reload.png" alt = "" title = "Swap items on page"><b>Swap Icon Style</b></a>
<a href = "/builder/"><img src="/media/icons/new/builder.png" alt = "WynnBuilder" title = "WynnBuilder"><b>WynnBuilder</b></a>
<a href = "/crafter/"><img src = "/media/icons/new/crafter.png" alt = "WynnCrafter" title = "WynnCrafter"><b>WynnCrafter</b></a>
<a href = "/items/"><img src = "/media/icons/new/searcher.png" alt = "WynnAtlas" title = "WynnAtlas"><b>WynnAtlas</b></a>
<a href = "/customizer.html"><img src = "/media/icons/new/custom.png" alt = "WynnCustom" title = "WynnCustom"><b>WynnCustom</b></a>
<a href = "/map.html"><img src = "/media/icons/new/compass.png" alt = "WynnGPS" title = "WynnGPS"><b>WynnGPS</b></a>
<a href = "/wynnfo/"><img src = "/media/icons/new/book.png" alt = "Wynnfo" title = "Wynnfo"><b>Wynnfo</b></a>
<a onclick = "toggleIcons()"><img src = "/media/icons/new/reload.png" alt = "" title = "Swap items on page"><b>Swap Icon Style</b></a>
<hr/>
<a href = "https://discord.gg/CGavnAnerv" target = "_blank"><img src = "../media/icons/discord.png" alt = "WB Discord" title = "WB Discord"><b>WB Discord</b></a>
</div>
@ -39,7 +39,7 @@
<a href="../credits.txt" class="link">Additional credits</a>
</div>
<div class = "col text-center" id = "help">
<a href="items_adv_help.html" class="link" target="_blank">Search Guide</a>
<a href="/wynnfo/items_adv_help/" class="link" target="_blank">Search Guide</a>
</div>
<div class = "col text-end">
<a href = "../items/">Basic Item Search</a>

196538
items_clean.json Normal file

File diff suppressed because it is too large Load diff

1
items_compress.json Normal file

File diff suppressed because one or more lines are too long

View file

@ -805,7 +805,6 @@ const atrees = {
"name": "Phantom Ray",
"base_spell": 1,
"spell_type": "damage",
"scaling": "spell",
"display": "Total Damage",
"parts": [
{
@ -2890,7 +2889,6 @@ const atrees = {
"cost": 40,
"base_spell": 1,
"spell_type": "damage",
"scaling": "spell",
"display": "Total Damage",
"parts": [
{
@ -3018,7 +3016,6 @@ const atrees = {
"cost": 25,
"base_spell": 2,
"spell_type": "damage",
"scaling": "spell",
"display": "",
"parts": []
}
@ -3281,7 +3278,6 @@ const atrees = {
"cost": 30,
"base_spell": 4,
"spell_type": "damage",
"scaling": "spell",
"display": "Total Damage",
"parts": [
{
@ -7361,9 +7357,6 @@ const atrees = {
"name": "Spin Attack",
"cost": 40,
"base_spell": 1,
"spell_type": "damage",
"scaling": "spell",
"use_atkspd": true,
"display": "Spin Attack",
"parts": [
{
@ -9052,7 +9045,7 @@ const atrees = {
},
{
"display_name": "Parry",
"desc": "After dodging damage, gain a brief damage buff, and make your next spell within 1.5 free. (3s Cooldown)",
"desc": "After dodging damage, gain a brief damage buff, and make your next spell within 1.5s free. (3s Cooldown)",
"archetype": "Acrobat",
"archetype_req": 5,
"parents": [
@ -9119,7 +9112,8 @@ const atrees = {
"Cheaper Dash II"
],
"dependencies": [
"Vanish"
"Vanish",
"Marked"
],
"blockers": [],
"cost": 2,
@ -9771,7 +9765,7 @@ const atrees = {
},
{
"display_name": "Nightcloak Knife",
"desc": "If cast while in Vanish, Spin Attack will consume all Marks from nearby enemies to summon the Nightcloak Knife.\n The Knife will mimic your attacks on enemies within 6 blocks of you, dealing 4% of your damage for every 1 Mark consumed. (Max 10 Marks)\n The Knife will vanish after 8s",
"desc": "If cast while in Vanish, Spin Attack will consume all Marks from nearby enemies to summon the Nightcloak Knife.\n The Knife will mimic your attacks on enemies within 6 blocks of you, dealing 4% of your damage for every 1 Mark consumed. (Max 10 Marks)\n The Knife will vanish after 15s",
"archetype": "Shadestepper",
"archetype_req": 0,
"parents": [
@ -9975,8 +9969,8 @@ const atrees = {
"type": "replace_spell",
"name": "Relik Melee",
"base_spell": 0,
"spell_type": "damage",
"scaling": "melee", "use_atkspd": false,
"scaling": "melee",
"use_atkspd": false,
"display": "Total",
"parts": [
{ "name": "Single Beam", "multipliers": [35, 0, 0, 0, 0, 0] },
@ -10090,8 +10084,8 @@ const atrees = {
"type": "replace_spell",
"name": "Relik Melee",
"base_spell": 0,
"spell_type": "damage",
"scaling": "melee", "use_atkspd": false,
"scaling": "melee",
"use_atkspd": false,
"display": "Total",
"parts": [
{ "name": "Single Beam", "multipliers": [34, 0, 0, 0, 0, 0] },
@ -10610,7 +10604,6 @@ const atrees = {
"type": "replace_spell",
"name": "Puppet Damage",
"base_spell": 6,
"scaling": "spell",
"display": "Total Puppet DPS",
"parts": [
{
@ -11401,7 +11394,6 @@ const atrees = {
"type": "replace_spell",
"name": "Twisted Tether",
"base_spell": 8,
"scaling": "spell",
"display": "Tether Tick",
"parts": [
{
@ -11588,7 +11580,7 @@ const atrees = {
"display_name": "Fluid Healing",
"desc": "For every 1% Water Damage you have from items, buff Aura's healing power by +0.3%.",
"parents": ["Twisted Tether", "Mask of the Coward", "Larger Blood Pool II"],
"dependencies": [],
"dependencies": [ "Sacrificial Shrine" ],
"blockers": [],
"cost": 2,
"display": {

File diff suppressed because one or more lines are too long

View file

@ -33,7 +33,8 @@ const wynn_version_names = [
'2.0.2.1',
'2.0.2.3',
'2.0.3.1',
'2.0.4.1'
'2.0.4.1',
'2.0.4.3'
];
const WYNN_VERSION_LATEST = wynn_version_names.length - 1;
// Default to the newest version.

View file

@ -827,9 +827,13 @@ class AggregateStatsNode extends ComputeNode {
}
}
let radiance_affected = [ /*"hp"*/, "fDef", "wDef", "aDef", "tDef", "eDef", "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", "rSdRaw", "sprint", "sprintReg", "jh", "lq", "gXp", "gSpd",
let radiance_affected = [ /*"hp"*/, "fDef", "wDef", "aDef", "tDef", "eDef", "hprPct", "mr", "sdPct", "mdPct", "ls", "ms",
// "xpb", "lb",
"ref",
/*"str", "dex", "int", "agi", "def",*/ // TODO its affected but i have to make it not affect req
"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", "rSdRaw", "sprint", "sprintReg", "jh",
// "lq", "gXp", "gSpd",
// wynn2 damages.
"eMdPct","eMdRaw","eSdPct","eSdRaw",/*"eDamPct,"*/"eDamRaw",//"eDamAddMin","eDamAddMax",
@ -868,12 +872,12 @@ const radiance_node = new (class extends ComputeNode {
}
}
}
const dam_mults = new Map(ret.get('damMult'));
dam_mults.set('tome', dam_mults.get('tome') * 1.2)
ret.set('damMult', dam_mults)
const def_mults = new Map(ret.get('defMult'));
def_mults.set('tome', def_mults.get('tome') * 1.2)
ret.set('defMult', def_mults)
// const dam_mults = new Map(ret.get('damMult'));
// dam_mults.set('tome', dam_mults.get('tome') * 1.2)
// ret.set('damMult', dam_mults)
// const def_mults = new Map(ret.get('defMult'));
// def_mults.set('tome', def_mults.get('tome') * 1.2)
// ret.set('defMult', def_mults)
return ret;
}
else {

View file

@ -1,4 +1,14 @@
{
"FISSION": {
"displayName": "Fission",
"description": "Explosions from your Exploding ID are twice as big and twice as strong",
"abilities": []
},
"EXPLOSIVE_IMPACT": {
"displayName": "Explosive Impact",
"description": "Your Exploding ID can trigger when hitting mobs with your Main Attack",
"abilities": []
},
"MAGNET": {
"displayName": "Magnet",
"description": "Pulls items within an 8 block radius towards you",
@ -214,7 +224,7 @@
"abilities": []
},
"DESC_FESTIVESPIRIT": {
"displayName": "Festive Spirits",
"displayName": "Festive Spirit",
"description": "Plays wintery tunes",
"abilities": []
},
@ -260,7 +270,7 @@
}]
},
"ALTEREGO": {
"displayName": "Alterego",
"displayName": "Alter Ego",
"description": "Awakened can be activated after saving 40% less mana, but its duration is reduced by 25%.",
"abilities": []
},
@ -296,7 +306,7 @@
}]
},
"SOUL_EATER": {
"displayName": "Soul eater",
"displayName": "Soul Eater",
"description": "Devour and Harvester grant double mana, but your maximum Marks are decreased by 1.",
"abilities": [{
"class": "Assassin",
@ -398,7 +408,7 @@
},
"DIVINE_HONOR": {
"displayName": "Divine Honor",
"description": "Increase the bonus from Radiance by 5%. Decrease the Neutral damage of Bash by -15%.",
"description": "Increase the bonus from Radiance by 5%. Decrease the Earth damage of Bash by -15%.",
"abilities": [{
"class": "Warrior",
"base_abil": "Bash",
@ -408,7 +418,7 @@
"base_spell": 1,
"target_part": "Single Hit",
"behavior": "modify",
"multipliers": [-15, 0, 0, 0, 0, 0]
"multipliers": [0, -15, 0, 0, 0, 0]
}
]
}]
@ -456,5 +466,67 @@
}
]
}]
},
"GRUESOME_KNOTS": {
"displayName": "Gruesome Knots",
"description":"Twisted Tether spends twice as much of your Blood Pool to deal triple damage",
"abilities": [{
"class": "Shaman",
"base_abil": "Twisted Tether",
"effects": [{
"type": "raw_stat",
"bonuses": [
{
"type": "stat",
"name": "damMult.GruesomeKnots:8.Tether Tick",
"value": 200
}
]
}]
}]
},
"COAGULATE": {
"displayName": "Coagulate",
"description":"Blood Connection launches you further and higher upon teleporting to a nearby Totem",
"abilities": []
},
"DEADWEIGHT": {
"displayName": "Dead Weight",
"description":"Totem's horizontal velocity is greatly increased at the cost of vertical movement",
"abilities": []
},
"EXPUNGE": {
"displayName": "Expunge",
"description":"When using Heal, instead cast one instant pulse that heals 20% of your max health",
"abilities": [{
"class": "Mage",
"base_abil": "Heal",
"effects": [
{
"type": "add_spell_prop",
"base_spell": 1,
"target_part": "Second and Third Pulses",
"behavior": "modify",
"power": -0.20
},
{
"type": "add_spell_prop",
"base_spell": 1,
"target_part": "Heal",
"behavior": "modify",
"power": 0.05
}
]
}]
},
"LUNGE": {
"displayName": "Lunge",
"description":"Hop's horizontal velocity is greatly increased",
"abilities": []
},
"WINDSURF": {
"displayName": "Windsurf",
"description":"Righting Reflex lasts twice as long and is affected stronger by movement speed",
"abilities": []
}
}

View file

@ -1,4 +1,4 @@
let recipeTypes = ["HELMET","CHESTPLATE","LEGGINGS","BOOTS","RELIK","WAND","SPEAR","DAGGER","BOW","RING","NECKLACE","BRACELET","SCROLL","FOOD","POTION"];
let recipeTypes = ["HELMET","CHESTPLATE","LEGGINGS","BOOTS","RELIK","WAND","SPEAR","DAGGER","BOW","RING","NECKLACE","BRACELET","POTION", "SCROLL","FOOD"];
let levelTypes = ["1-3","3-5","5-7","7-9","10-13","13-15","15-17","17-19","20-23","23-25","25-27","27-29","30-33","33-35","35-37","37-39","40-43","43-45","45-47","47-49","50-53","53-55","55-57","57-59","60-63","63-65","65-67","67-69","70-73","73-75","75-77","77-79","80-83","83-85","85-87","87-89","90-93","93-95","95-97","97-99","100-103","103-105",]
function encodeCraft(craft) {

View file

@ -144,8 +144,8 @@ function calculateSpellDamage(stats, weapon, _conversions, use_spell_damage, ign
// Collect total damage post %boost
}
let total_elem_min = total_min - damages[0][0];
let total_elem_max = total_max - damages[0][1];
let total_elem_min = total_min - save_prop[0][0];
let total_elem_max = total_max - save_prop[0][1];
// 5.2: Raw application.
let prop_raw = stats.get(specific_boost_str.toLowerCase()+'Raw') + stats.get('damRaw');

105
js/docs.js Normal file
View file

@ -0,0 +1,105 @@
/** Vanilla JS does not allow dynamically fetching files in a local directory. We have to hard code the list to loop over.
* Each entry is the name of a subfolder in /docs/.
*/
const docs_post_names = [
"items_adv_help",
"damage_calc",
"powders"
]
/** Map of
* Key: post_name - the subdirectory name also present in docs_post_names
* Value: [Title, Summary, Author(s)]
*/
docs_post_thumbnails = new Map();
docs_post_thumbnails.set("items_adv_help", ["Advanced Item Search Help", "A practical guide on how to use WynnBuilder's advanced item search feature.", "Phanta"]);
docs_post_thumbnails.set("damage_calc", ["Damage Calculation", "All the ins and outs of Wynncraft damage calculation. Includes spells, powder specials, abilities, and major IDs!", "hppeng, ferricles"]);
docs_post_thumbnails.set("powders", ["Weapon Powder Application", "Read this to learn the mechanics of powder application on weapons!", "ferricles, hppeng"]);
/** Populates the HTML element with the id 'post-list'
*
*/
function populate_post_previews() {
post_list_parent = document.getElementById('post-list');
docs_post_names.forEach((post_name) => {
post = document.createElement('article');
post_info = docs_post_thumbnails.get(post_name);
console.log(post_info);
if (post_info == undefined) {
return;
}
title = document.createElement('h2');
title.innerHTML = post_info[0];
post.appendChild(title);
summary = document.createElement('p');
summary.innerHTML = post_info[1];
post.appendChild(summary);
authors = document.createElement('p');
authors.innerHTML = "Author(s): " + post_info[2];
post.appendChild(authors);
link = document.createElement("a");
link.setAttribute('href', `/wynnfo/${post_name}/`);
post.appendChild(link);
post_list_parent.appendChild(post);
});
}
function initDropdownSections() {
dropdowns = document.querySelectorAll('span.dropdown');
for (const dropdown of dropdowns) {
let inner_content = dropdown.children[0]
dropdown.classList.add("up", "row");
let title = document.createElement("div");
title.classList.add("col-10", "item-title", "text-start", "dropdown-title")
title.style.margin = "0 0 0";
title.style.fontWeight = "bold";
title.style.fontSize = 18;
title.style.textDecoration = "underline";
title.textContent = dropdown.title;
dropdown.textContent = "";
let indicator = document.createElement("div");
indicator.classList.add("col-auto", "fw-bold", "box-title");
indicator.textContent = "+";
dropdown.prepend(indicator);
dropdown.prepend(title);
dropdown.appendChild(inner_content);
inner_content.style.display = "none";
title.addEventListener("click", function(){
if (dropdown.classList.contains("up")) {
dropdown.classList.remove("up");
dropdown.classList.add("down");
indicator.textContent = "-";
inner_content.style.display = "";
} else {
dropdown.classList.remove("down");
dropdown.classList.add("up");
indicator.textContent = "+";
inner_content.style.display = "none";
}
});
title.addEventListener("mouseover", function(){
title.style.color = "#ddd";
// title.style.fontWeight = "bold";
indicator.style.color = "#ddd";
// indicator.style.fontWeight = "bold";
});
title.addEventListener("mouseleave", function(){
title.style.color = "";
// title.style.fontWeight = "normal";
indicator.style.color = "";
// indicator.style.fontWeight = "normal";
});
}
}

View file

@ -47,54 +47,54 @@ const translate_mappings = {
"Soul Point Regen": "spRegen",
"Stealing": "eSteal",
"Raw Health Regen": "hprRaw",
"Spell Damage Raw: ": "sdRaw",
"Elem. Spell Damage Raw: ": "rSdRaw",
"Neut. Spell Damage Raw: ": "nSdRaw",
"Earth Spell Damage Raw: ": "eSdRaw",
"Thunder Spell Damage Raw: ": "tSdRaw",
"Water Spell Damage Raw: ": "wSdRaw",
"Fire Spell Damage Raw: ": "fSdRaw",
"Air Spell Damage Raw: ": "aSdRaw",
"Spell Damage %: ": "sdPct",
"Elem. Spell Damage %: ": "rSdPct",
"Neut. Spell Damage %: ": "nSdPct",
"Earth Spell Damage %: ": "eSdPct",
"Thunder Spell Damage %: ": "tSdPct",
"Water Spell Damage %: ": "wSdPct",
"Fire Spell Damage %: ": "fSdPct",
"Air Spell Damage %: ": "aSdPct",
"Melee Damage Raw: ": "mdRaw",
"Elem. Melee Damage Raw: ": "rMdRaw",
"Neut. Melee Damage Raw: ": "nMdRaw",
"Earth Melee Damage Raw: ": "eMdRaw",
"Thunder Melee Damage Raw: ": "tMdRaw",
"Water Melee Damage Raw: ": "wMdRaw",
"Fire Melee Damage Raw: ": "fMdRaw",
"Air Melee Damage Raw: ": "aMdRaw",
"Melee Damage %: ": "mdPct",
"Elem. Melee Damage %: ": "rMdPct",
"Neut. Melee Damage %: ": "nMdPct",
"Earth Melee Damage %: ": "eMdPct",
"Thunder Melee Damage %: ": "tMdPct",
"Water Melee Damage %: ": "wMdPct",
"Fire Melee Damage %: ": "fMdPct",
"Air Melee Damage %: ": "aMdPct",
"Damage Raw: ": "damRaw",
"Elemental Damage Raw: ": "rDamRaw",
"Neutral Damage Raw: ": "nDamRaw",
"Earth Damage Raw: ": "eDamRaw",
"Thunder Damage Raw: ": "tDamRaw",
"Water Damage Raw: ": "wDamRaw",
"Fire Damage Raw: ": "fDamRaw",
"Air Damage Raw: ": "aDamRaw",
"Damage %: ": "damPct",
"Elemental Damage %: ": "rDamPct",
"Neutral Damage %: ": "nDamPct",
"Earth Damage %: ": "eDamPct",
"Thunder Damage %: ": "tDamPct",
"Water Damage %: ": "wDamPct",
"Fire Damage %: ": "fDamPct",
"Air Damage %: ": "aDamPct",
"Spell Damage Raw": "sdRaw",
"Elem. Spell Damage Raw": "rSdRaw",
"Neut. Spell Damage Raw": "nSdRaw",
"Earth Spell Damage Raw": "eSdRaw",
"Thunder Spell Damage Raw": "tSdRaw",
"Water Spell Damage Raw": "wSdRaw",
"Fire Spell Damage Raw": "fSdRaw",
"Air Spell Damage Raw": "aSdRaw",
"Spell Damage %": "sdPct",
"Elem. Spell Damage %": "rSdPct",
"Neut. Spell Damage %": "nSdPct",
"Earth Spell Damage %": "eSdPct",
"Thunder Spell Damage %": "tSdPct",
"Water Spell Damage %": "wSdPct",
"Fire Spell Damage %": "fSdPct",
"Air Spell Damage %": "aSdPct",
"Melee Damage Raw": "mdRaw",
"Elem. Melee Damage Raw": "rMdRaw",
"Neut. Melee Damage Raw": "nMdRaw",
"Earth Melee Damage Raw": "eMdRaw",
"Thunder Melee Damage Raw": "tMdRaw",
"Water Melee Damage Raw": "wMdRaw",
"Fire Melee Damage Raw": "fMdRaw",
"Air Melee Damage Raw": "aMdRaw",
"Melee Damage %": "mdPct",
"Elem. Melee Damage %": "rMdPct",
"Neut. Melee Damage %": "nMdPct",
"Earth Melee Damage %": "eMdPct",
"Thunder Melee Damage %": "tMdPct",
"Water Melee Damage %": "wMdPct",
"Fire Melee Damage %": "fMdPct",
"Air Melee Damage %": "aMdPct",
"Damage Raw": "damRaw",
"Elemental Damage Raw": "rDamRaw",
"Neutral Damage Raw": "nDamRaw",
"Earth Damage Raw": "eDamRaw",
"Thunder Damage Raw": "tDamRaw",
"Water Damage Raw": "wDamRaw",
"Fire Damage Raw": "fDamRaw",
"Air Damage Raw": "aDamRaw",
"Damage %": "damPct",
"Elemental Damage %": "rDamPct",
"Neutral Damage %": "nDamPct",
"Earth Damage %": "eDamPct",
"Thunder Damage %": "tDamPct",
"Water Damage %": "wDamPct",
"Fire Damage %": "fDamPct",
"Air Damage %": "aDamPct",
"% Fire Defense": "fDefPct",
"% Water Defense": "wDefPct",

View file

@ -56,54 +56,54 @@ const translate_mappings = {
"Soul Point Regen": "spRegen",
"Stealing": "eSteal",
"Raw Health Regen": "hprRaw",
"Spell Damage Raw: ": "sdRaw",
"Elem. Spell Damage Raw: ": "rSdRaw",
"Neut. Spell Damage Raw: ": "nSdRaw",
"Earth Spell Damage Raw: ": "eSdRaw",
"Thunder Spell Damage Raw: ": "tSdRaw",
"Water Spell Damage Raw: ": "wSdRaw",
"Fire Spell Damage Raw: ": "fSdRaw",
"Air Spell Damage Raw: ": "aSdRaw",
"Spell Damage %: ": "sdPct",
"Elem. Spell Damage %: ": "rSdPct",
"Neut. Spell Damage %: ": "nSdPct",
"Earth Spell Damage %: ": "eSdPct",
"Thunder Spell Damage %: ": "tSdPct",
"Water Spell Damage %: ": "wSdPct",
"Fire Spell Damage %: ": "fSdPct",
"Air Spell Damage %: ": "aSdPct",
"Melee Damage Raw: ": "mdRaw",
"Elem. Melee Damage Raw: ": "rMdRaw",
"Neut. Melee Damage Raw: ": "nMdRaw",
"Earth Melee Damage Raw: ": "eMdRaw",
"Thunder Melee Damage Raw: ": "tMdRaw",
"Water Melee Damage Raw: ": "wMdRaw",
"Fire Melee Damage Raw: ": "fMdRaw",
"Air Melee Damage Raw: ": "aMdRaw",
"Melee Damage %: ": "mdPct",
"Elem. Melee Damage %: ": "rMdPct",
"Neut. Melee Damage %: ": "nMdPct",
"Earth Melee Damage %: ": "eMdPct",
"Thunder Melee Damage %: ": "tMdPct",
"Water Melee Damage %: ": "wMdPct",
"Fire Melee Damage %: ": "fMdPct",
"Air Melee Damage %: ": "aMdPct",
"Damage Raw: ": "damRaw",
"Elemental Damage Raw: ": "rDamRaw",
"Neutral Damage Raw: ": "nDamRaw",
"Earth Damage Raw: ": "eDamRaw",
"Thunder Damage Raw: ": "tDamRaw",
"Water Damage Raw: ": "wDamRaw",
"Fire Damage Raw: ": "fDamRaw",
"Air Damage Raw: ": "aDamRaw",
"Damage %: ": "damPct",
"Elemental Damage %: ": "rDamPct",
"Neutral Damage %: ": "nDamPct",
"Earth Damage %: ": "eDamPct",
"Thunder Damage %: ": "tDamPct",
"Water Damage %: ": "wDamPct",
"Fire Damage %: ": "fDamPct",
"Air Damage %: ": "aDamPct",
"Spell Damage Raw": "sdRaw",
"Elem. Spell Damage Raw": "rSdRaw",
"Neut. Spell Damage Raw": "nSdRaw",
"Earth Spell Damage Raw": "eSdRaw",
"Thunder Spell Damage Raw": "tSdRaw",
"Water Spell Damage Raw": "wSdRaw",
"Fire Spell Damage Raw": "fSdRaw",
"Air Spell Damage Raw": "aSdRaw",
"Spell Damage %": "sdPct",
"Elem. Spell Damage %": "rSdPct",
"Neut. Spell Damage %": "nSdPct",
"Earth Spell Damage %": "eSdPct",
"Thunder Spell Damage %": "tSdPct",
"Water Spell Damage %": "wSdPct",
"Fire Spell Damage %": "fSdPct",
"Air Spell Damage %": "aSdPct",
"Melee Damage Raw": "mdRaw",
"Elem. Melee Damage Raw": "rMdRaw",
"Neut. Melee Damage Raw": "nMdRaw",
"Earth Melee Damage Raw": "eMdRaw",
"Thunder Melee Damage Raw": "tMdRaw",
"Water Melee Damage Raw": "wMdRaw",
"Fire Melee Damage Raw": "fMdRaw",
"Air Melee Damage Raw": "aMdRaw",
"Melee Damage %": "mdPct",
"Elem. Melee Damage %": "rMdPct",
"Neut. Melee Damage %": "nMdPct",
"Earth Melee Damage %": "eMdPct",
"Thunder Melee Damage %": "tMdPct",
"Water Melee Damage %": "wMdPct",
"Fire Melee Damage %": "fMdPct",
"Air Melee Damage %": "aMdPct",
"Damage Raw": "damRaw",
"Elemental Damage Raw": "rDamRaw",
"Neutral Damage Raw": "nDamRaw",
"Earth Damage Raw": "eDamRaw",
"Thunder Damage Raw": "tDamRaw",
"Water Damage Raw": "wDamRaw",
"Fire Damage Raw": "fDamRaw",
"Air Damage Raw": "aDamRaw",
"Damage %": "damPct",
"Elemental Damage %": "rDamPct",
"Neutral Damage %": "nDamPct",
"Earth Damage %": "eDamPct",
"Thunder Damage %": "tDamPct",
"Water Damage %": "wDamPct",
"Fire Damage %": "fDamPct",
"Air Damage %": "aDamPct",
"% Fire Defense": "fDefPct",
"% Water Defense": "wDefPct",

View file

@ -1,4 +1,4 @@
const DB_VERSION = 129;
const DB_VERSION = 130;
// @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 = 28;
const ING_DB_VERSION = 31;
// @See https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/video-store/index.js

View file

@ -167,6 +167,14 @@ const itemQueryProps = (function() {
maxId(['lootbonus', 'lb'], 'lb');
maxId(['xpbonus', 'xpb', 'xb'], 'xpb');
maxId(['stealing', 'esteal'], 'eSteal');
maxId(['lq', 'quality'], 'lq');
maxId('gxp', 'gXp');
maxId('gspd', 'gSpd');
maxId(['healeff', 'healpct'], 'healPct');
maxId('kb', 'kb');
maxId('weakenenemy', 'weakenEnemy');
maxId('slowenemy', 'slowEnemy');
prop(['powderslots', 'powders', 'slots', 'sockets'], 'number', (i, ie) => i.slots || 0);
return props;

View file

@ -3847,5 +3847,26 @@
"Pain Cycle": 3845,
"Psionic Pretense": 3846,
"Propeller Hat": 3847,
"Tremorcaller": 3848
"Tremorcaller": 3848,
"Black Skull": 3849,
"Replica Hallowynn Mask": 3850,
"Esteemed Bronze Mask of Legendary Victory": 3851,
"Swashbuckler's Brogues": 3852,
"Outlandish Replica Face Mask of Legendary Victory": 3853,
"Mystical Tags": 3854,
"Future Shock Plating": 3855,
"Spider Leg Suit": 3856,
"Ceremonial Skirt": 3857,
"Runebound Chains": 3858,
"Ice-cold Robe": 3859,
"Blob Monster Mask": 3860,
"Psychopomp's Pileus": 3861,
"Revered Silver Mask of Legendary Victory": 3862,
"Treasured Diamond Mask of Legendary Victory": 3863,
"Venerated Gold Mask of Legendary Victory": 3864,
"Beige Wynnter Sweater": 3865,
"Scarlet Wynnter Sweater": 3866,
"Orange Wynnter Sweater": 3867,
"Pine Wynnter Sweater": 3868,
"Indigo Wynnter Sweater": 3869
}

View file

@ -0,0 +1,231 @@
{
"identifications": [
"rawMainAttackDamage",
"rawSpellDamage",
"healthRegenRaw",
"manaSteal",
"walkSpeed",
"thunderDamage",
"rawStrength",
"rawDexterity",
"rawIntelligence",
"rawDefence",
"rawAgility",
"lootBonus",
"fireDefence",
"airDefence",
"mainAttackDamage",
"spellDamage",
"exploding",
"airDamage",
"rawHealth",
"reflection",
"earthDefence",
"earthDamage",
"waterDamage",
"waterDefence",
"healthRegen",
"manaRegen",
"fireDamage",
"lifeSteal",
"rawAttackSpeed",
"xpBonus",
"thunderDefence",
"thorns",
"soulPointRegen",
"stealing",
"1stSpellCost",
"2ndSpellCost",
"raw1stSpellCost",
"raw3rdSpellCost",
"jumpHeight",
"airSpellDamage",
"poison",
"elementalDamage",
"raw4thSpellCost",
"raw2ndSpellCost",
"sprintRegen",
"slowEnemy",
"3rdSpellCost",
"sprint",
"elementalSpellDamage",
"rawNeutralSpellDamage",
"4thSpellCost",
"healingEfficiency",
"knockback",
"waterSpellDamage",
"fireSpellDamage",
"rawAirMainAttackDamage",
"rawAirSpellDamage",
"earthSpellDamage",
"rawThunderDamage",
"rawWaterDamage",
"rawElementalDamage",
"rawEarthSpellDamage",
"elementalDefence",
"rawThunderMainAttackDamage",
"thunderSpellDamage",
"rawThunderSpellDamage",
"rawFireMainAttackDamage",
"weakenEnemy",
"rawWaterSpellDamage",
"earthMainAttackDamage",
"rawFireSpellDamage",
"rawElementalSpellDamage",
"rawElementalMainAttackDamage",
"airMainAttackDamage",
"thunderMainAttackDamage",
"leveledLootBonus",
"damageFromMobs",
"leveledXpBonus",
"elementalDefense",
"rawAirDamage",
"rawEarthDamage",
"rawFireDamage",
"rawNeutralDamage",
"lootQuality",
"gatherXpBonus",
"gatherSpeed",
"rawWaterMainAttackDamage"
],
"majorIds": [
"Divine Honor",
"Greed",
"Magnet",
"Plague",
"Saviour\u2019s Sacrifice",
"Sorcery",
"Soul Eater",
"Cherry Bombs",
"Expunge",
"Flashfreeze",
"Gentle Glow",
"Gruesome Knots",
"Peaceful Effigy",
"Rally",
"Reckless Abandon",
"Gravity Well",
"Perfect Recall",
"Cavalryman",
"Entropy",
"Escape Route",
"Lightweight",
"Snowy Steps",
"Taunt",
"Dead Weight",
"Furious Effigy",
"Geocentrism",
"Strings of Fate",
"Alter Ego",
"Explosive Impact",
"Festive Spirit",
"Freerunner",
"Hawkeye",
"Windsurf",
"Juggle",
"Roving Assassin",
"Transcendence",
"Fission",
"Forest's Blessing",
"Heart of the Pack",
"Coagulate",
"Guardian",
"Overwhelm",
"Temblor",
"Lunge",
"Madness"
],
"filters": {
"type": [
"weapons",
"armour",
"accessories",
"tomes",
"charms",
"tools",
"ingredients",
"materials"
],
"advanced": {
"attackSpeed": [
"super_slow",
"very_slow",
"slow",
"normal",
"fast",
"very_fast",
"super_fast"
],
"weapons": [
"bow",
"relik",
"wand",
"dagger",
"spear"
],
"armour": [
"helmet",
"chestplate",
"leggings",
"boots"
],
"accessories": [
"necklace",
"ring",
"bracelet"
],
"tomes": [
"weaponTome",
"armourTome",
"slayingTome",
"dungeonTome",
"gatheringTome",
"guildTome",
"lootrunTome"
],
"tools": [
"axe",
"pickaxe",
"rod",
"scythe"
],
"crafting": [
"alchemism",
"armouring",
"cooking",
"jeweling",
"scribing",
"tailoring",
"weaponsmithing",
"woodworking"
],
"gathering": [
"mining",
"fishing",
"farming",
"woodcutting"
]
},
"tier": {
"items": [
"common",
"fabled",
"legendary",
"mythic",
"rare",
"set",
"unique"
],
"ingredients": [
0,
1,
2,
3
]
},
"levelRange": {
"items": 110,
"ingredients": 105
}
}
}

114
py_script/item_wrapper.py Normal file
View file

@ -0,0 +1,114 @@
"""
Description: Quick item save/search with v3 item database
API Documentation: https://documentation.wynncraft.com/docs/
Update item db: python item_wrapper.py update-item [file_directory]
Item search: python item_wrapper.py search -keyword [War] -itemType [mythic] ...
"""
import requests
import json
import argparse
class Items:
"""v3 item wrapping - Synchronous"""
def fetch(self, url):
response = requests.get(url)
return response.json()
def post(self, url, data=None):
response = requests.post(url, json=data)
return response.json()
def get_all_items(self):
api_url = "https://api.wynncraft.com/v3/item/database?fullResult=True"
return self.fetch(api_url)
def get_metadata(self):
url = "https://api.wynncraft.com/v3/item/metadata"
return self.fetch(url)
def item_query(self, data=None):
api_url = "https://api.wynncraft.com/v3/item/search?fullResult=True"
return self.post(api_url, data)
def update_items(file_path):
data = Items().get_all_items()
update_file(data, file_path)
print(f"{len(data)} items updated")
def update_metadata(file_path):
data = Items().get_metadata()
update_file(data, file_path)
print("Metadata updated")
def update_file(input, output):
try:
with open(output, "w") as file:
json.dump(input, file, indent=3)
except Exception as error:
print(f"File update error: {error}")
def item_search_param(keyword=None, itemType=None, itemTier=None, atkSpeed=None, lvlRange=None, prof=None, ids=None, majorId=None):
payload = {
"query": [] if keyword is None else keyword,
"type": [] if itemType is None else itemType,
"tier": [] if itemTier is None else itemTier,
"attackSpeed": [] if atkSpeed is None else atkSpeed,
"levelRange": [] if lvlRange is None else lvlRange,
"professions": [] if prof is None else prof,
"identifications": [] if ids is None else ids,
"majorIds": [] if majorId is None else majorId
}
try:
response = Items().item_query(payload)
print(json.dumps(response, indent=3))
# Save the response as needed
except requests.RequestException as error:
print(f"Request error: {error}")
def main():
parser = argparse.ArgumentParser(description='Wynncraft Item API Script')
subparsers = parser.add_subparsers(dest='command', help='Pick your poison')
update_items_parser = subparsers.add_parser('update-items', help='Update all items')
update_items_parser.add_argument('file', help='File path for saving item json')
update_metadata_parser = subparsers.add_parser('update-metadata', help='Update metadata')
update_metadata_parser.add_argument('file', help='File path for saving metadata json')
search_parser = subparsers.add_parser('search', help='Search for items with parameters')
search_parser.add_argument('-keyword', type=str, default=None, help='Keyword for item search')
search_parser.add_argument('-itemType', type=str, default=None, help='Item type: wand, bow, etc')
search_parser.add_argument('-itemTier', type=str, default=None, help='Item tier: mythic, legendary, etc')
search_parser.add_argument('-atkSpeed', type=str, default=None, help='Attack speed param')
search_parser.add_argument('-lvlRange', nargs=2, type=int, default=None, help='Level range for: min, max')
search_parser.add_argument('-prof', type=str, default=None, help='Professions (Ing)')
search_parser.add_argument('-ids', type=str, default=None, help='Identifications field')
search_parser.add_argument('-majorId', type=str, default=None, help='Major IDs')
args = parser.parse_args()
if args.command == 'update-items':
update_items(args.file)
elif args.command == 'update-metadata':
update_metadata(args.file)
elif args.command == 'search':
item_search_param(
keyword=args.keyword,
itemType=args.itemType,
itemTier=args.itemTier,
atkSpeed=args.atkSpeed,
lvlRange=args.lvlRange,
prof=args.prof,
ids=args.ids,
majorId=args.majorId
)
if __name__ == "__main__":
main()

View file

@ -1,140 +1,6 @@
translate_mappings = {
#"name": "name",
#"displayName": "displayName",
#"tier": "tier",
#"set": "set",
"sockets": "slots",
#"type": "type",
#"armorType": "armorType", (deleted)
"armorColor": "color", #(deleted)
"addedLore": "lore", #(deleted)
#"material": "material", (deleted)
"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",
#"bonusFireDamage": "fDamPct",
#"bonusWaterDamage": "wDamPct",
#"bonusAirDamage": "aDamPct",
#"bonusThunderDamage": "tDamPct",
#"bonusEarthDamage": "eDamPct",
"fireDamageBonus": "fDamPct",
"waterDamageBonus": "wDamPct",
"airDamageBonus": "aDamPct",
"thunderDamageBonus": "tDamPct",
"earthDamageBonus": "eDamPct",
"elementalDamageBonus": "rDamPct",
"fireDamageBonusRaw": "fDamRaw",
"waterDamageBonusRaw": "wDamRaw",
"airDamageBonusRaw": "aDamRaw",
"thunderDamageBonusRaw": "tDamRaw",
"earthDamageBonusRaw": "eDamRaw",
"elementalDamageBonusRaw": "rDamRaw",
"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",
"healingEfficiency": "healPct",
"knockback": "kb",
"weakenEnemy": "weakenEnemy",
"slowEnemy": "slowEnemy",
"elementalDefense": "rDefPct",
}
import json
with open("translate_mappings.json", 'r') as infile:
translate_mappings = json.load(infile)
delete_keys = [
#"addedLore",
@ -143,3 +9,36 @@ delete_keys = [
#"armorColor",
#"material"
]
if __name__ == "__main__":
# SELF TEST: compare dict keys with `item_metadata.json`.
from item_wrapper import Items
local_metadata_file = "item_metadata.json"
def debug(*args, **kwargs):
print(*args, **kwargs)
try:
debug("updating item metadata...")
metadata_check = Items().get_metadata()
with open(local_metadata_file, 'w') as outfile:
json.dump(metadata_check, outfile, indent=2)
except:
debug("Could not update item metadata. using local wynn metadata")
with open(local_metadata_file, 'r') as infile:
metadata_check = json.load(infile)
checklist = set(x for x in translate_mappings.keys())
debug(f"Checking {len(checklist)} identifications")
n = 0
for identification in metadata_check['identifications']:
if identification in checklist:
checklist.remove(identification)
else:
print(f"WARNING: id not accounted for: {identification}")
n += 1
debug(f"{n} unmapped API identifications.")
for identification in checklist:
print(f"WARNING: unused translate map entry {identification}")
debug(f"{len(checklist)} unused translation entries.")

View file

@ -187,7 +187,7 @@ for ing in ings:
"notTouching": 0
}
if 'itemIDs' not in ing:
ing['consumableIDs'] = {
ing['itemIDs'] = {
"dura": 0,
"strReq": 0,
"dexReq": 0,

View file

@ -15,15 +15,16 @@ import sys
import os
import base64
import argparse
from items_common import translate_mappings, delete_keys
#from items_common import translate_mappings, delete_keys
parser = argparse.ArgumentParser(description="Process raw pulled item data.")
parser.add_argument('infile', help='input file to read data from')
parser.add_argument('infile', help='input file to read data from', default=None, nargs='?')
parser.add_argument('outfile', help='output file to dump clean data into')
args = parser.parse_args()
infile, outfile = args.infile, args.outfile
if args.infile is None:
print("Grabbing json data from wynn api")
with open(infile, "r") as in_file:
with open(args.infile, "r") as in_file:
data = json.loads(in_file.read())
@ -65,8 +66,9 @@ for item in items:
for k, v in translate_mappings.items():
if k in item:
item[v] = item[k]
tmp = item[k]
del item[k]
item[v] = tmp
if not (item["name"] in id_map):
while max_id in used_ids:

View file

@ -0,0 +1,189 @@
{
"ingredient": {
"name": "displayName",
"internalName": "name",
"tier": "tier",
"material": "DELETE;",
"skin": "DELETE;",
"itemOnlyIDs": "RECURSE_ingredient.itemIDs;itemIDs",
"consumableOnlyIDs": "RECURSE_ingredient.consumableIDs;consumableIDs",
"ingredientPositionModifiers": "RECURSE_ingredient.posMods;posMods",
"identifications": "RECURSE_identifications;ids",
"droppedBy": "DELETE;",
"___droppedBy_comment:": "Deleting for now because it is way too large and we cannot use it in a meaningful way. But this is nice data for when wynnatlas ever gets updated",
"requirements": "UNWRAP;requirements"
},
"ingredient.itemIDs": {
"durabilityModifier": "dura",
"strengthRequirement": "strReq",
"dexterityRequirement": "dexReq",
"intelligenceRequirement": "intReq",
"defenceRequirement": "defReq",
"agilityRequirement": "agiReq",
"attackSpeedModifier": "atkSpdMod",
"powderSlotModifier": "slotMod"
},
"ingredient.consumableIDs": {
"duration": "dura",
"charges": "charges"
},
"ingredient.posMods": {
"left": "left",
"right": "right",
"above": "above",
"under": "under",
"touching": "touching",
"notTouching": "notTouching"
},
"item": {
"name": "displayName",
"internalName": "name",
"tier": "CAPS;tier",
"set": "set",
"powderSlots": "slots",
"type": "type",
"armorType": "armorType",
"material": "material",
"dropRestriction": "drop",
"dropMeta": "dropInfo",
"quest": "quest",
"restrictions": "restrict",
"accessoryType": "type",
"identified": "fixID",
"skin": "skin",
"category": "category",
"attackSpeed": "ALLCAPS;atkSpd",
"base": "UNWRAP;item.base",
"requirements": "UNWRAP;requirements",
"identifications": "UNWRAP;identifications"
},
"item.base": {
"damage": "STR_RANGE;nDam",
"fireDamage": "STR_RANGE;fDam",
"waterDamage": "STR_RANGE;wDam",
"airDamage": "STR_RANGE;aDam",
"thunderDamage": "STR_RANGE;tDam",
"earthDamage": "STR_RANGE;eDam",
"health": "hp",
"fireDefence": "fDef",
"waterDefence": "wDef",
"airDefence": "aDef",
"thunderDefence": "tDef",
"earthDefence": "eDef",
"averageDPS": "DELETE;"
},
"requirements": {
"level": "lvl",
"levelRange": "lvRange",
"classRequirement": "classReq",
"strength": "strReq",
"dexterity": "dexReq",
"intelligence": "intReq",
"agility": "agiReq",
"defence": "defReq"
},
"identifications": {
"healthRegen": "hprPct",
"manaRegen": "mr",
"spellDamage": "sdPct",
"elementalSpellDamage": "rSdPct",
"neutralSpellDamage": "nSdPct",
"fireSpellDamage": "fSdPct",
"waterSpellDamage": "wSdPct",
"airSpellDamage": "aSdPct",
"thunderSpellDamage": "tSdPct",
"earthSpellDamage": "eSdPct",
"mainAttackDamage": "mdPct",
"elementalMainAttackDamage": "rMdPct",
"neutralMainAttackDamage": "nMdPct",
"fireMainAttackDamage": "fMdPct",
"waterMainAttackDamage": "wMdPct",
"airMainAttackDamage": "aMdPct",
"thunderMainAttackDamage": "tMdPct",
"earthMainAttackDamage": "eMdPct",
"lifeSteal": "ls",
"manaSteal": "ms",
"xpBonus": "xpb",
"lootBonus": "lb",
"leveledXpBonus": "lxpb",
"leveledLootBonus": "llb",
"reflection": "ref",
"rawStrength": "str",
"rawDexterity": "dex",
"rawIntelligence": "int",
"rawDefence": "def",
"rawAgility": "agi",
"thorns": "thorns",
"poison": "poison",
"exploding": "expd",
"walkSpeed": "spd",
"rawAttackSpeed": "atkTier",
"rawHealth": "hpBonus",
"soulPointRegen": "spRegen",
"stealing": "eSteal",
"healthRegenRaw": "hprRaw",
"rawSpellDamage": "sdRaw",
"rawElementalSpellDamage": "rSdRaw",
"rawNeutralSpellDamage": "nSdRaw",
"rawFireSpellDamage": "fSdRaw",
"rawWaterSpellDamage": "wSdRaw",
"rawAirSpellDamage": "aSdRaw",
"rawThunderSpellDamage": "tSdRaw",
"rawEarthSpellDamage": "eSdRaw",
"rawMainAttackDamage": "mdRaw",
"rawElementalMainAttackDamage": "rMdRaw",
"rawNeutralMainAttackDamage": "nMdRaw",
"rawFireMainAttackDamage": "fMdRaw",
"rawWaterMainAttackDamage": "wMdRaw",
"rawAirMainAttackDamage": "aMdRaw",
"rawThunderMainAttackDamage": "tMdRaw",
"rawEarthMainAttackDamage": "eMdRaw",
"damage": "damPct",
"neutralDamage": "nDamPct",
"fireDamage": "fDamPct",
"waterDamage": "wDamPct",
"airDamage": "aDamPct",
"thunderDamage": "tDamPct",
"earthDamage": "eDamPct",
"elementalDamage": "rDamPct",
"rawDamage": "damRaw",
"rawNeutralDamage": "nDamRaw",
"rawFireDamage": "fDamRaw",
"rawWaterDamage": "wDamRaw",
"rawAirDamage": "aDamRaw",
"rawThunderDamage": "tDamRaw",
"rawEarthDamage": "eDamRaw",
"rawElementalDamage": "rDamRaw",
"fireDefence": "fDefPct",
"waterDefence": "wDefPct",
"airDefence": "aDefPct",
"thunderDefence": "tDefPct",
"earthDefence": "eDefPct",
"elementalDefence": "rDefPct",
"1stSpellCost": "spPct1",
"raw1stSpellCost": "spRaw1",
"2ndSpellCost": "spPct2",
"raw2ndSpellCost": "spRaw2",
"3rdSpellCost": "spPct3",
"raw3rdSpellCost": "spRaw3",
"4thSpellCost": "spPct4",
"raw4thSpellCost": "spRaw4",
"sprint": "sprint",
"sprintRegen": "sprintReg",
"jumpHeight": "jh",
"lootQuality": "lq",
"gatherXpBonus": "gXp",
"gatherSpeed": "gSpd",
"healingEfficiency": "healPct",
"knockback": "kb",
"weakenEnemy": "weakenEnemy",
"slowEnemy": "slowEnemy",
"elementalDefense": "rDefPct",
"damageFromMobs": "selfWeakPct"
}
}

View file

@ -0,0 +1,236 @@
"""
Used to process the raw item data pulled from the API.
Usage:
- python process_items.py [infile] [outfile]
OR
- python process_items.py [infile and outfile]
NOTE: id_map.json is due for change. Should be updated manually when Wynn2.0/corresponding WB version drops.
"""
import json
import sys
import os
import base64
import argparse
from items_common import translate_mappings
parser = argparse.ArgumentParser(description="Process raw pulled item data.")
parser.add_argument('infile', help='input file to read data from', default=None, nargs='?')
#parser.add_argument('outfile', help='output file to dump clean data into')
args = parser.parse_args()
if args.infile is None:
print("Grabbing json data from wynn api")
from item_wrapper import Items
api_data = Items().get_all_items()
json.dump(api_data, open('dump.json', 'w'))
else:
with open(args.infile, "r") as in_file:
api_data = json.load(in_file)
def translate_single_item(key, entry, name, directives, accumulate):
ret = entry
try:
if 'min' in entry and 'max' in entry:
if 'raw' in entry:
ret = entry['raw']
else:
ret = [entry['min'], entry['max']]
except:
pass
i = 0
while i < len(directives):
directive = directives[i]
if directive == 'DELETE':
ret = None
elif directive == 'CAPS':
ret = ret[0].upper() + ret[1:]
elif directive == 'ALLCAPS':
ret = ret.upper()
elif directive == 'STR_RANGE':
if 'min' in entry and 'max' in entry:
ret = f"{entry['min']}-{entry['max']}"
elif directive == 'UNWRAP':
recursive_translate(entry, accumulate, name, translate_single_item)
ret = None
i += 1
return ret
def translate_single_ing(key, entry, name, directives, accumulate):
ret = entry
try:
if 'min' in entry and 'max' in entry:
ret = {
'minimum': entry['min'],
'maximum': entry['max']
}
except:
pass
i = 0
while i < len(directives):
directive = directives[i]
if directive == 'DELETE':
ret = None
elif directive == 'UNWRAP':
recursive_translate(entry, accumulate, name, translate_single_ing)
ret = None
elif directive[:8] == 'RECURSE_':
ret = recursive_translate(entry, {}, directive[8:], translate_single_ing)
i += 1
return ret
def recursive_translate(entry, result, path, translate_single):
mapping = translate_mappings[path]
for k, v in entry.items():
# Translate the item.
if k in mapping:
tmp = mapping[k].split(';')
directives, translated_name = tmp[:-1], tmp[-1]
res = translate_single(k, v, translated_name, directives, result)
if res is not None:
result[translated_name] = res
continue
# pass it through unchanged.
result[k] = v
return result
armor_types = ['helmet', 'chestplate', 'leggings', 'boots']
def translate_entry(entry):
"""
Convert an api entry into an appropriate parsed item.
Returns a pair: (converted, type)
where `type` is "item", "ingredient", "tome", "material", "charm", or None
and converted might be None if the conversion failed.
"""
# sketchily infer what kind of item we're dealing with, and translate it appropriately.
if "type" in entry:
# only items have this field.
res = recursive_translate(entry, {}, "item", translate_single_item)
if res['type'] in armor_types:
res['category'] = 'armor'
else:
res['category'] = 'weapon'
for element in 'netwfa':
damage_key = element + 'Dam'
if damage_key not in res:
res[damage_key] = '0-0'
return res, 'item'
if "accessoryType" in entry:
# only accessories have this field.
return recursive_translate(entry, {'category': 'accessory'}, "item", translate_single_item), "item"
if "itemOnlyIDs" in entry:
# only ingredients have this field.
res = recursive_translate(entry, {}, "ingredient", translate_single_ing)
return res, "ingredient"
#return recursive_translate(entry, {}, "ing"), "ingredient"
if "tomeType" in entry:
# only tomes have this field.
return None, "tome"
if "craftable" in entry:
return None, "material"
# I think the only things left are charms, we just don't classify them.
return None, None
with open("id_map.json", "r") as id_map_file:
id_map = json.load(id_map_file)
used_ids = set([v for k, v in id_map.items()])
max_id = 0
with open("ing_map.json","r") as ing_map_file:
ing_map = json.load(ing_map_file)
items = []
ingreds = []
for name, entry in api_data.items():
entry['name'] = name
res, entry_type = translate_entry(entry)
print(f"Parsed {name}, type {entry_type}")
if res is None:
continue
# TODO: make this a map or smth less ugly code
if entry_type == 'item':
items.append(res)
elif entry_type == 'ingredient':
ingreds.append(res)
with open("../clean.json", "r") as oldfile:
old_data = json.load(oldfile)
old_items = old_data['items']
with open("../ingreds_clean.json", "r") as ingfile:
old_ingreds = json.load(ingfile)
known_item_names = set()
known_ingred_names = set()
for item in items:
known_item_names.add(item["name"])
for ingred in ingreds:
known_ingred_names.add(ingred["name"])
for item in old_items:
if item["name"] not in known_item_names:
print(f'Unknown old item: {item["name"]}!!!')
for ingred in old_ingreds:
if ingred["name"] not in known_ingred_names:
print(f'Unknown old ingred: {ingred["name"]}!!!')
# TODO hack pull the major id file
major_ids_filename = "../js/builder/major_ids_clean.json"
with open(major_ids_filename, 'r') as major_ids_file:
major_ids_map = json.load(major_ids_file)
major_ids_reverse_map = { v['displayName'] : k for k, v in major_ids_map.items() }
for item in items:
# HACKY ITEM FIXES!
if 'majorIds' in item:
item['majorIds'] = [ major_ids_reverse_map[item['majorIds']['name']] ]
if not (item["name"] in id_map):
while max_id in used_ids:
max_id += 1
used_ids.add(max_id)
id_map[item["name"]] = max_id
print(f'New item: {item["name"]} (id: {max_id})')
item["id"] = id_map[item["name"]]
for ingred in ingreds:
# HACKY ING FIXES!
ingred['itemIDs']['dura'] = int(ingred['itemIDs']['dura'] / 1000)
ingred['skills'] = [x.upper() for x in ingred['skills']]
if 'ids' not in ingred:
ingred['ids'] = dict()
print(f"ing missing 'ids': {ingred['name']}")
if 'consumableIDs' not in ingred:
ingred['consumableIDs'] = {'dura': 0, 'charges': 0}
print(f"ing missing 'consumableIDs': {ingred['name']}")
if not (ingred["name"] in ing_map):
new_id = len(ing_map)
ing_map[ingred["name"]] = new_id
print(f'New item: {item["name"]} (id: {new_id})')
ingred["id"] = ing_map[ingred["name"]]
#write items back into data
old_data["items"] = items
#save id map
with open("id_map.json","w") as id_map_file:
json.dump(id_map, id_map_file, indent=2)
with open("ing_map.json","w") as ing_map_file:
json.dump(ing_map, ing_map_file, indent=2)
#write the data back to the outfile
with open('item_out.json', "w+") as out_file:
json.dump(old_data, out_file, ensure_ascii=False, separators=(',', ':'))
with open('ing_out.json', "w+") as out_file:
json.dump(ingreds, out_file, ensure_ascii=False, separators=(',', ':'))

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

27
thirdparty/katex.js vendored Normal file
View file

@ -0,0 +1,27 @@
function add_katex() {
head_elem = document.getElementsByTagName("head")[0];
link = document.createElement('link')
link.setAttribute('rel', 'stylesheet')
link.setAttribute('href', "https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.css")
link.setAttribute('integrity', "sha384-wcIxkf4k558AjM3Yz3BBFQUbk/zgIYC2R0QpeeYb+TwlBVMrlgLqwRjRtGZiK7ww")
link.setAttribute('crossorigin', "anonymous")
scr_1 = document.createElement('script')
scr_1.setAttribute('src', "https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.js")
scr_1.setAttribute('integrity', "sha384-hIoBPJpTUs74ddyc4bFZSM1TVlQDA60VBbJS0oA934VSz82sBx1X7kSx2ATBDIyd")
scr_1.setAttribute('crossorigin', "anonymous")
scr_1.setAttribute('defer', '')
scr_2 = document.createElement('script')
scr_2.setAttribute('src',"https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/contrib/auto-render.min.js")
scr_2.setAttribute('integrity', "sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk")
scr_2.setAttribute('crossorigin', "anonymous")
scr_2.setAttribute('defer', '')
scr_2.setAttribute('onload', "renderMathInElement(document.body, {delimiters: [{left: '$$', right: '$$', display: true},{left: '$', right: '$', display: false}]});")
head_elem.append(link, scr_1, scr_2);
}
add_katex();

View file

@ -0,0 +1,53 @@
<!DOCTYPE html>
<html scroll-behavior="smooth">
<head>
<title>WynnAtlas Help</title>
<link rel="icon" href="/media/icons/new/searcher.png">
<link rel="manifest" href="manifest.json">
<meta name="viewport" content="width=device-width, initial-scale=.45, user-scalable=no">
<!-- nunito font, copying wynnbuilder, which is copying wynndata -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
<link href="/thirdparty/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link rel="stylesheet" href="/thirdparty/autoComplete.min.css">
<link rel="stylesheet" href="/css/article.css"> <!-- This one is the important one for docs -->
<link rel="stylesheet" href="/css/docs_home.css">
<link rel="stylesheet" href="/css/styles.css">
<link rel="stylesheet" href="/css/sidebar.css">
<link rel="stylesheet" href="/css/wynnstyles.css">
<script type="text/javascript" src="/thirdparty/katex.js"></script>
</head>
<body class="all" style="overflow-y: scroll">
<div id="main-sidebar" class="sidebar dark-7 dark-shadow">
<a href = "/builder/"><img src="/media/icons/new/builder.png" alt = "WynnBuilder" title = "WynnBuilder"><b>WynnBuilder</b></a>
<a href = "/crafter/"><img src = "/media/icons/new/crafter.png" alt = "WynnCrafter" title = "WynnCrafter"><b>WynnCrafter</b></a>
<a href = "/items/"><img src = "/media/icons/new/searcher.png" alt = "WynnAtlas" title = "WynnAtlas"><b>WynnAtlas</b></a>
<a href = "/custom/"><img src = "/media/icons/new/custom.png" alt = "WynnCustom" title = "WynnCustom"><b>WynnCustom</b></a>
<a href = "/map/"><img src = "/media/icons/new/compass.png" alt = "WynnGPS" title = "WynnGPS"><b>WynnGPS</b></a>
<a href = "/wynnfo/"><img src = "/media/icons/new/book.png" alt = "Wynnfo" title = "Wynnfo"><b>Wynnfo</b></a>
<a onclick = "toggleIcons()"><img src = "/media/icons/new/reload.png" alt = "" title = "Swap items on page"><b>Swap Icon Style</b></a>
<hr/>
<a href = "https://discord.gg/CGavnAnerv" target = "_blank"><img src = "/media/icons/discord.png" alt = "WB Discord" title = "WB Discord"><b>WB Discord</b></a>
</div>
<main>
<div class="full-width text-center">
<!-- <img src="./help_photo.png"/> -->
</div>
<div class="section">
<h2>Lorem Ipsum</h2>
<p>Hopefully this gets replced by a guide to Wynn 2.1 damage calculation</p>
</div>
</main>
<script type="text/javascript" src="/js/icons.js"></script>
</body>
</html>

View file

@ -1,85 +1,58 @@
<!DOCTYPE html>
<html scroll-behavior="smooth">
<head>
<title>WynnAtlas</title>
<link rel="icon" href="../media/icons/new/searcher.png" type="image/icon type">
<title>WynnDocs</title>
<link rel="icon" href="/media/icons/new/searcher.png">
<link rel="manifest" href="manifest.json">
<meta name="viewport" content="width=device-width, initial-scale=.45, user-scalable=no">
<!-- nunito font, copying wynnbuilder, which is copying wynndata -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
<link href="/thirdparty/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link rel="stylesheet" href="/thirdparty/autoComplete.min.css">
<link rel="stylesheet" href="../css/sq2bs.css">
<link rel="stylesheet" href="../css/sidebar.css">
<link rel="stylesheet" href="../css/wynnstyles.css">
<link rel="stylesheet" href="/css/article.css"> <!-- This one is the important one for docs -->
<link rel="stylesheet" href="/css/docs_home.css">
<link rel="stylesheet" href="/css/styles.css">
<link rel="stylesheet" href="/css/sidebar.css">
<link rel="stylesheet" href="/css/wynnstyles.css">
<script text = "text/javascript" src = "/js/docs.js"></script>
<script text = "text/javascript">document.addEventListener("DOMContentLoaded", (event) => {populate_post_previews();})</script>
</head>
<body class = "text-light d-flex justify-content-center" id = "body">
<div id="main-sidebar" class="sidebar dark-7 dark-shadow">
<a href = "../builder/"><img src="../media/icons/new/builder.png" alt = "WynnBuilder" title = "WynnBuilder"><b>WynnBuilder</b></a>
<a href = "../crafter/"><img src = "../media/icons/new/crafter.png" alt = "WynnCrafter" title = "WynnCrafter"><b>WynnCrafter</b></a>
<a href = "../items/"><img src = "../media/icons/new/searcher.png" alt = "WynnAtlas" title = "WynnAtlas"><b>WynnAtlas</b></a>
<a href = "../custom/"><img src = "../media/icons/new/custom.png" alt = "WynnCustom" title = "WynnCustom"><b>WynnCustom</b></a>
<a href = "../map/"><img src = "../media/icons/new/compass.png" alt = "WynnGPS" title = "WynnGPS"><b>WynnGPS</b></a>
<a href = ""><img src = "../media/icons/new/book.png" alt = "Wynnfo" title = "Wynnfo"><b>Wynnfo</b></a>
<a onclick = "toggleIcons()"><img src = "../media/icons/new/reload.png" alt = "" title = "Swap items on page"><b>Swap Icon Style</b></a>
<hr/>
<a href = "https://discord.gg/CGavnAnerv" target = "_blank"><img src = "../media/icons/discord.png" alt = "WB Discord" title = "WB Discord"><b>WB Discord</b></a>
<body class="all" style="overflow-y: scroll">
<div id="main-sidebar" class="sidebar dark-7 dark-shadow">
<a href = "/builder/"><img src="/media/icons/new/builder.png" alt = "WynnBuilder" title = "WynnBuilder"><b>WynnBuilder</b></a>
<a href = "/crafter/"><img src = "/media/icons/new/crafter.png" alt = "WynnCrafter" title = "WynnCrafter"><b>WynnCrafter</b></a>
<a href = "/items/"><img src = "/media/icons/new/searcher.png" alt = "WynnAtlas" title = "WynnAtlas"><b>WynnAtlas</b></a>
<a href = "/custom/"><img src = "/media/icons/new/custom.png" alt = "WynnCustom" title = "WynnCustom"><b>WynnCustom</b></a>
<a href = "/map/"><img src = "/media/icons/new/compass.png" alt = "WynnGPS" title = "WynnGPS"><b>WynnGPS</b></a>
<a href = "."><img src = "/media/icons/new/book.png" alt = "Wynnfo" title = "Wynnfo"><b>Wynnfo</b></a>
<a onclick = "toggleIcons()"><img src = "/media/icons/new/reload.png" alt = "" title = "Swap items on page"><b>Swap Icon Style</b></a>
<hr/>
<a href = "https://discord.gg/CGavnAnerv" target = "_blank"><img src = "/media/icons/discord.png" alt = "WB Discord" title = "WB Discord"><b>WB Discord</b></a>
</div>
<main>
<div class="full-width text-center">
<img src="/media/items/new.png"/>
</div>
<div id="mobile-navbar" class="navbar dark-5 dark-shadow fixed-top d-lg-none pb-0">
<div class="container-fluid scaled-font justify-content-center" style="height: 5vh;">
<div class="navbar-brand mx-auto scaled-font" style="height: 100%;">
<img src="../media/icons/new/book.png" alt="" style="height: 100%;">
<span>WynnFo</span>
</div>
<button class="btn dropdown-toggle dark-2 px-4 text-white scaled-font border-dark border-3" onclick="toggle_tab('mobile-navbar-dropdown');"></button>
</div>
<div class="container-fluid scaled-font dark-3 px-3 py-3" id="mobile-navbar-dropdown" style="display: none;">
<a href="../builder/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/builder.png" alt="" style="height: 100%;">
<span>WynnBuilder</span>
</a>
<a href="../crafter/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/crafter.png" alt="" style="height: 100%;">
<span>WynnCrafter</span>
</a>
<a href="../items/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/searcher.png" alt="" style="height: 100%;">
<span>WynnAtlas</span>
</a>
<a href="../custom/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/custom.png" alt="" style="height: 100%;">
<span>WynnCustom</span>
</a>
<a href="../map/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/compass.png" alt="" style="height: 100%;">
<span>WynnGPS</span>
</a>
<a href="../wynnfo/" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/book.png" alt="" style="height: 100%;">
<span>WynnFo</span>
</a>
<a onclick = "toggleIcons()" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/new/reload.png" alt="" style="height: 100%;">
<span>Swap Icon Style</span>
</a>
<a href="https://discord.gg/CGavnAnerv" class="w-100 mb-3 text-white" style="height: 5vh; text-decoration: none;">
<img src="../media/icons/discord.png" alt="" style="height: 100%;">
<span>Discord</span>
</a>
</div>
<div class="section">
<h2>Welcome to the WynnBuilder Docs</h2>
<p>Documentation Homepage - sections coming soon!</p>
</div>
<div class = "container py-5 vh-100 mx-0 mx-lg-auto scaled-font mt-lg-2" id = "main" style="margin-top: 6vh;">
<div class = "row item-title">Welcome!</div>
<p class = "row">This page is the main page for Wynnfo. Wynnfo is Wynnbuilder's page for all sorts of Wynncraft-related literature, including code documenation, game mechanic novels, and other literature! Browse at your leisure below.</p>
<!-- Gets populated by populate_post_previews() -->
<div class ="section" id = "post-list">
</div>
<script type="text/javascript" src = "scripts/main.js"></script>
<script type="text/javascript" src = "../js/utils.js"></script>
<script type="text/javascript" src = "../js/icons.js"></script>
</body>
</main>
<script type="text/javascript" src="/js/icons.js"></script>
</body>
</html>

View file

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 65 KiB

View file

@ -2,7 +2,7 @@
<html scroll-behavior="smooth">
<head>
<title>WynnAtlas Help</title>
<link rel="icon" href="../media/icons/new/searcher.png">
<link rel="icon" href="/media/icons/new/searcher.png">
<link rel="manifest" href="manifest.json">
<meta name="viewport" content="width=device-width, initial-scale=.45, user-scalable=no">
@ -18,28 +18,31 @@
<link rel="stylesheet" href="/thirdparty/autoComplete.min.css">
<link rel="stylesheet" href="../css/article.css">
<link rel="stylesheet" href="../css/styles.css">
<link rel="stylesheet" href="../css/sidebar.css">
<link rel="stylesheet" href="../css/wynnstyles.css">
<link rel="stylesheet" href="/css/article.css"> <!-- This one is the important one for docs -->
<link rel="stylesheet" href="/css/styles.css">
<link rel="stylesheet" href="/css/sidebar.css">
<link rel="stylesheet" href="/css/wynnstyles.css">
</head>
<body class="all" style="overflow-y: scroll">
<div id="main-sidebar" class="sidebar dark-7 dark-shadow">
<a href = "../builder/"><img src="../media/icons/new/builder.png" alt = "WynnBuilder" title = "WynnBuilder"><b>WynnBuilder</b></a>
<a href = "../crafter/"><img src = "../media/icons/new/crafter.png" alt = "WynnCrafter" title = "WynnCrafter"><b>WynnCrafter</b></a>
<a href = "../items/"><img src = "../media/icons/new/searcher.png" alt = "WynnAtlas" title = "WynnAtlas"><b>WynnAtlas</b></a>
<a href = "../custom/"><img src = "../media/icons/new/custom.png" alt = "WynnCustom" title = "WynnCustom"><b>WynnCustom</b></a>
<a href = "../map/"><img src = "../media/icons/new/compass.png" alt = "WynnGPS" title = "WynnGPS"><b>WynnGPS</b></a>
<a href = "../wynnfo/"><img src = "../media/icons/new/book.png" alt = "Wynnfo" title = "WynnCrafter"><b>WynnCrafter</b></a>
<a onclick = "toggleIcons()"><img src = "../media/icons/new/reload.png" alt = "" title = "Swap items on page"><b>Swap Icon Style</b></a>
<a href = "/builder/"><img src="/media/icons/new/builder.png" alt = "WynnBuilder" title = "WynnBuilder"><b>WynnBuilder</b></a>
<a href = "/crafter/"><img src = "/media/icons/new/crafter.png" alt = "WynnCrafter" title = "WynnCrafter"><b>WynnCrafter</b></a>
<a href = "/items/"><img src = "/media/icons/new/searcher.png" alt = "WynnAtlas" title = "WynnAtlas"><b>WynnAtlas</b></a>
<a href = "/custom/"><img src = "/media/icons/new/custom.png" alt = "WynnCustom" title = "WynnCustom"><b>WynnCustom</b></a>
<a href = "/map/"><img src = "/media/icons/new/compass.png" alt = "WynnGPS" title = "WynnGPS"><b>WynnGPS</b></a>
<a href = "/wynnfo/"><img src = "/media/icons/new/book.png" alt = "Wynnfo" title = "Wynnfo"><b>Wynnfo</b></a>
<a onclick = "toggleIcons()"><img src = "/media/icons/new/reload.png" alt = "" title = "Swap items on page"><b>Swap Icon Style</b></a>
<hr/>
<a href = "https://discord.gg/CGavnAnerv" target = "_blank"><img src = "../media/icons/discord.png" alt = "WB Discord" title = "WB Discord"><b>WB Discord</b></a>
<a href = "https://discord.gg/CGavnAnerv" target = "_blank"><img src = "/media/icons/discord.png" alt = "WB Discord" title = "WB Discord"><b>WB Discord</b></a>
</div>
<main>
<div class="full-width text-center">
<img src="./help_photo.png"/>
</div>
<div class="section">
<h1>Advanced Item Search Guide</h1>
</div>
<div class="section">
<h2>What the heck is &ldquo;Advanced Item Search&rdquo;?</h2>
<p>The WynnBuilder team has been hard at work giving you the latest and greatest tools for optimizing your most complex Wynncraft builds. Now, we're introducing <strong class="rb-text">WynnAtlas</strong>, the new, bigger, better, smarter, powerful-er item guide! Featuring an extremely flexible expression language for filtering and sorting items, WynnAtlas' advanced item search system gives build engineers the power to select items with a high degree of granularity. Picking components for your brand-new Divzer WFA build has never been easier!</p>
@ -221,6 +224,6 @@
docsFns.append(genDocEntry(entry[0], entry[1], null, entry[2]));
}
</script>
<script type="text/javascript" src="../js/icons.js"></script>
<script type="text/javascript" src="/js/icons.js"></script>
</body>
</html>

267
wynnfo/powders/index.html Normal file
View file

@ -0,0 +1,267 @@
<!DOCTYPE html>
<html scroll-behavior="smooth">
<head>
<title>Powder Mechanics Guide</title>
<link rel="icon" href="/media/icons/new/searcher.png">
<link rel="manifest" href="manifest.json">
<meta name="viewport" content="width=device-width, initial-scale=.45, user-scalable=no">
<!-- nunito font, copying wynnbuilder, which is copying wynndata -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">
<link href="/thirdparty/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link rel="stylesheet" href="/thirdparty/autoComplete.min.css">
<link rel="stylesheet" href="/css/article.css"> <!-- This one is the important one for docs -->
<link rel="stylesheet" href="/css/docs_home.css">
<link rel="stylesheet" href="/css/styles.css">
<link rel="stylesheet" href="/css/sidebar.css">
<link rel="stylesheet" href="/css/wynnstyles.css">
<script type="text/javascript" src="/js/docs.js"></script>
<script text = "text/javascript">document.addEventListener("DOMContentLoaded", (event) => {initDropdownSections();})</script>
</head>
<body class="all" style="overflow-y: scroll">
<div id="main-sidebar" class="sidebar dark-7 dark-shadow">
<a href = "/builder/"><img src="/media/icons/new/builder.png" alt = "WynnBuilder" title = "WynnBuilder"><b>WynnBuilder</b></a>
<a href = "/crafter/"><img src = "/media/icons/new/crafter.png" alt = "WynnCrafter" title = "WynnCrafter"><b>WynnCrafter</b></a>
<a href = "/items/"><img src = "/media/icons/new/searcher.png" alt = "WynnAtlas" title = "WynnAtlas"><b>WynnAtlas</b></a>
<a href = "/custom/"><img src = "/media/icons/new/custom.png" alt = "WynnCustom" title = "WynnCustom"><b>WynnCustom</b></a>
<a href = "/map/"><img src = "/media/icons/new/compass.png" alt = "WynnGPS" title = "WynnGPS"><b>WynnGPS</b></a>
<a href = "/wynnfo/"><img src = "/media/icons/new/book.png" alt = "Wynnfo" title = "Wynnfo"><b>Wynnfo</b></a>
<a onclick = "toggleIcons()"><img src = "/media/icons/new/reload.png" alt = "" title = "Swap items on page"><b>Swap Icon Style</b></a>
<hr/>
<a href = "https://discord.gg/CGavnAnerv" target = "_blank"><img src = "/media/icons/discord.png" alt = "WB Discord" title = "WB Discord"><b>WB Discord</b></a>
</div>
<main>
<div class="full-width text-center">
<!-- <img src="./help_photo.png"/> -->
</div>
<div class="section">
<h1>Powder Application Guide</h1>
<p>June 5, 2024 (Wynn 2.0)</p>
<p>Authors: ferricles, hppeng</p>
</div>
<div class="section">
<h2>Basics</h2>
<p>I assume you know what powders do - you apply them on armor to get elemental defense or on weapons to get extra damage. If you apply multiple high-tier powders of the same type, you get an extra ability on your weapon (powder special). In this post, we'll go over the calculations that get run when powders are applied on weapons.</p>
<p>
Here's some terminology that we'll use later:
<li>Base damage range: the low and high damage ranges which are shown in the item display. </li>
<li>Damage type: There are 6 types of damage. There is neutral damage (<buf class = 'Neutral'>N</buf>) and 5 elemental types: earth (<buf class = 'Earth'>E</buf>), thunder (<buf class = 'Thunder'>T</buf>), water (<buf class = 'Water'>W</buf>), fire (<buf class = 'Fire'>F</buf>), and air (<buf class = 'Air'>A</buf>). </li>
<li>Conversion (<code>powder.conversion</code>): The percentage conversion rate of a powder from neutral to elemental damage.</li>
<li>Powder raw (<code>powder.raw</code>): The raw boost to the base damage range of an element given by a powder.</li>
</p>
<p>Before we get into calculations, one last thing - there are two types of weapons we can powder: non-crafted (regular) and crafted. There are also two corresponding types of powder application which have their own mechanics: applied powders, when a powder master puts powders on your items, and ingredient powders, when you use a powder in the recipe to make a crafted item. We will start with the more common weapon type: regular weapons.</p>
</div>
<div class="section">
<h2>Powder Application on Regular Weapons</h2>
<p>On regular weapons, only the applied powder mechanic is relevant. The algorithm for this mechanic can be broken down into two steps: the aggregation step and the application step.</p>
<p>
Loop one (aggregation step):
<li>Record the order of powders in order of <b>first powder for each type</b>.</li>
<li>Aggregate conversion stats for each element additively.</li>
</p>
<p>
Loop two (application step):
<li>In the order of elemental types determined in loop one, calculate powder conversion of <b>remaining neutral damage</b>.</li>
<li>Neutral damage range is decreased by the total conversion range</li>
<li>Elemental damage range is increased by the total conversion range</li>
<li>Elemental damage range is also increased by the powder range</li>
<li>At the end of all applications, the damage ranges are floored for display on the weapon. (Note: these values are not floored when performing damage calculations later)</li>
</p>
<p>In pseudocode, this process looks like:</p>
<pre>
powders = [] //populated with powders which have a type, a conversion rate, and a range
order = []
conversions = {type: {raw: [low, high], conversion: float }}
for powder in powders:
If powder.type not in order:
order += powder.type
conversions[powder.type][raw] += powder.raw
conversions[powder.type][conversion] += powder.conversion
for powder_type in order:
powder_conversion = conversions[powder_type]
neu_conversion = (powder_conversion.conversion * weapon_neu_range)
weapon_neu_range -= neu_conversion
weapon_elem_range += neu_conversion + powder_conversion.range //the appropriate elemental type</pre>
<p>We verified this algorithm by testing in-game. If you're interested, click on this dropdown for a worked example!</p>
<span class="dropdown" style="font-weight:bold;"title="Regular Applied Powder Example">
<div>
<p>We used a <a class = "link" href = "/item/#Crystal Senbon">Crystal Senbon</a> to test. Crystal Senbon has 5 powder slots and a base damage range of <tmp class = 'Neutral'>[77, 77]</tmp>.</p>
<p>The powders we applied, in order: [<Earth>III</Earth>, <Thunder>III</Thunder>, <Water>III</Water>, <Earth>III</Earth>, <Earth>III</Earth>]</p>
<p>And their stats:</p>
<li><Earth>III</Earth>: [8, 14] raw, 25% conversion </li>
<li><Thunder>III</Thunder>: [2, 18] raw, 13% conversion </li>
<li><Water>III</Water>: [6, 10] raw, 17% conversion </li>
<p>We can start running the applied powder algorithm. After the aggregation step, we'd have:</p>
<pre>
order = [E, T, W]
conversions: {
E: {raw: [24, 42], conversion: 0.75},
T: {raw: [2, 18], conversion: 0.13},
W: {raw: [6, 10], conversion: 0.17}
}</pre>
<p>Then we can run through the application step.</p>
<pre>
Starting neutral range = <Neutral>[77, 77]</Neutral>
Earth conversion: [77, 77] * .75 = 57.75
<Neutral></Neutral>= [77, 77] - [57.75, 57.75] = <Neutral>[19.25, 19.25]</Neutral>
<Earth></Earth>= [57.75, 57.75] + [24, 42] = <Earth>[81.75, 99.75]</Earth>
Thunder conversion: [19.25, 19.25] * .13 = 2.5025
<Neutral></Neutral>= [19.25, 19.25] - [2.5025, 2.5025] = <Neutral>[16.7475, 16.7475]</Neutral>
<Thunder></Thunder>= [2.5025, 2.5025] + [2, 18] = <Thunder>[4.5025, 20.5025]</Thunder>
Water conversion: [16.7475, 16.7475] * 0.17 = 2.847075
<Neutral></Neutral>= [16.7475, 16.7475] - [2.847075, 2.847075] = <Neutral>[13.900425, 13.900425]</Neutral>
<Water></Water>= [2.847075, 2.847075] + [6, 10] = <Water>[8.847, 12.847]</Water>
Final (displayed) ranges:
floor(<Neutral>[13.900425, 13.900425]</Neutral>) = <Neutral>[13, 13]</Neutral>
floor(<Earth>[81.75, 99.75]</Earth>) = <Earth>[81, 99]</Earth>
floor(<Thunder>[4.5025, 20.5025]</Thunder>) = <Thunder>[4, 20]</Thunder>
floor(<Water>[8.847, 12.847]</Water>) = <Water>[8, 12]</Water></pre>
<p>After flooring all damage values, the Crystal Senbon should display damage ranges of <Neutral>[13, 13]</Neutral> <Earth>[43, 19]</Earth> <Thunder>[4, 20]</Thunder> <Water>[8, 12]</Water>. However, our weapon display suggests otherwise! This is because it is lying.</p>
<div class="text-center fig">
<img src="./media/crystal_senbon_powdered.png" width = "300"/>
</div>
<p>We collected some data samples by melee-ing some combat dummies many times (on the order of hundreds). Showing photo proof for many hits in this post is excessive (we show one for good measure). We tested this on a character with 0 abilities, 0 external buffs, and only the skill points necessary for holding Crystal Senbon (10 in each).</p>
<p>This is somewhat handwaving damage calculation, but here's the expected ranges of an assassin melee attack using the weapon damages we calculated.</p>
<p>Stat bonuses:
<li>3% earth damage boost (crystal senbon id)</li>
<li>7% thunder damage boost (crystal senbon id)</li>
<li>4% water damage boost (crystal senbon id)</li>
<li>11.3% earth dmg boost (12 str)</li>
<li>11.3% thunder dmg boost (12 dex)</li>
<li>7.0% water dmg boost (12 int)</li>
<li>11.3% total damage boost (12 strength)</li>
</p>
<p>The damage ranges we would expect to see from using a melee attack using our calculated base damage ranges:</p>
<p><code><Neutral>[13.900425, 13.900425]</Neutral> * (1 + .113) ~= <Neutral>[15.4707, 15.4707]</Neutral></code> </p>
<p><code><Earth>[81.75, 99.75]</Earth> * (1 + 0.03 + 0.113) * (1 + 0.113) ~= <Earth>[103.999, 126.898]</Earth></code></p>
<p><code><Thunder>[4.5025, 20.5025]</Thunder> * (1 + 0.07 + 0.113) * (1 + 0.113) ~= <Thunder>[5.928, 26.995]</Thunder></code></p>
<p><code><Water>[8.847, 12.847]</Water> * (1 + 0.04 + 0.07) * (1 + 0.113) ~= <Water>[10.930, 15.872]</Water></code></p>
<div class="text-center fig">
<img src="./media/crystal_senbon_attack.png" width = "500"/>
<div class = "caption">Note: the double damage hit is due to a dexterity crit.</div>
</div>
<p>Evidently, the observed damages match up much more closely the powder/damage calculations we've performed compared to the advertised damages on the weapon. Consider the fact that the weapon display doesn't even show neutral damage...</p>
</div>
</span>
</div>
<div class="section">
<h2>Powder Application on Crafted Weapons</h2>
<p>There are 2 algorithms relevant to powder mechanics on crafted weapons. First, we'll cover the ingredient powder mechanic.</p>
<p>The ingredient powder mechanic:</p>
<pre>
powders = [crafting menu powders in order of reading english]
for powder in powders:
conversion = floor(neutral_remaining * powder.conversion)
Neutral_base_dmg -= conversion
Elem_base_dmg += conversion + floor((powder.raw.low + powder.raw.high) / 2)</pre>
<p>For those unfamiliar with crafted weapon base damage: crafted weapons have a singular base damage value that is randomly chosen from a range when the item is crafted. The resulting base damage range is calculated using this value: if <code>base</code> is the base damage value, then the corresponding range is <code>[floor(0.9 * base), floor(1.1 * base)]</code>. The ingredient powder mechanic modifies the base damage values of a crafted weapon before they get converted to base damage ranges.</p>
<p>If you use a powder as an ingredient in a crafting recipe for a crafted weapon, the above ingredient powder mechanic is applied. However, there's a more complex interaction when using both ingredient and applied powders. Using the ingredient powder mechanic and the applied powder mechanic as subroutines, we can define the algorithm for applying powders on crafted weapons as below:</p>
<ul>1) Apply all applied powders with the ingredient powder mechanic.</ul>
<ul>2) Apply all ingredient powders with the ingredient powder mechanic.</ul>
<ul>3) Use the base damage values of the crafted weapon to get a full damage range.</ul>
<ul>4) Apply all applied powders with the applied powder mechanic.</ul>
<p>Both the ingredient powder mechanic and the full algorithm for crafted weapon powders were tested and verified in-game. For another worked-out example, expand the following dropdown.</p>
<span class="dropdown" title="Crafted Weapon Powder Example">
<div>
<p>We crafted a wand using a <Air>III</Air> powder as an ingredient. Here's a <a href = "/crafter/#1+x+W+W+W+W+W9q10" class = "link">link to the recipe</a>. We then applied these powders, in order: [<Water>I</Water>, <Fire>I</Fire>, <Water>I</Water>]. Powder stats below:</p>
<li><Air>III</Air>: [4, 11] raw, 17% conversion</li>
<li><Water>I</Water>: [3, 4] raw, 13% conversion</li>
<li><Fire>I</Fire>: [2, 5] raw, 14% conversion</li>
<div class="text-center fig">
<img src="./media/CR_prepowder.png" width = "500"/>
<div class = "caption">The crafted weapon pre-applied powders.</div>
</div>
<p>The crafted weapon starts with 181 base (before applying any powders through any mechanic). We will run through the entire crafted weapon powder application process below:</p>
<pre>
Starting Neutral Base: <Neutral>181</Neutral>
1) Apply applied powders with ingredient powder mechanic
<Neutral></Neutral>-= floor(.13 * 181) = 181 - 23 = <Neutral>158</Neutral>
<Water></Water>+= 23 + floor((3+4)/2) = 23 + 3 = <Water>26</Water>
<Neutral></Neutral>-= floor(.14 * 158) = 158 - 22 = <Neutral>136</Neutral>
<Fire></Fire>+= 22 + floor((2 + 5)/2) = 22 + 3 = <Fire>25</Fire>
<Neutral></Neutral>-= floor(.13 * 136) = 136 - 17 = <Neutral>119</Neutral>
<Water></Water>+= 17 + floor((3 + 4) / 2) = 26 + 17 + 3 = <Water>46</Water>
2) Apply ingredient powders with ingredient powder mechanic
<Neutral></Neutral>-= floor(.17 * 119) = 119 - 20 = <Neutral>99</Neutral>
<Air></Air>+= 20 + floor((4 + 11) / 2) = 20 + 7 = <Air>27</Air>
3) Convert base damage to ranges
floor(<Neutral>99</Neutral> * [0.9, 1.1]) = <Neutral>[89, 108]</Neutral>
floor(<Water>46</Water> * [0.9, 1.1]) = <Water>[41, 50]</Water>
floor(<Fire>25</Fire> * [0.9, 1.1]) = <Fire>[22, 27]</Fire>
floor(<Air>27</Air> * [0.9, 1.1]) = <Air>[24, 29]</Air>
4) Applied applied powders with applied powder mechanic
Starting base ranges:
<Neutral>[89, 108]</Neutral>
<Water>[41, 50]</Water>
<Fire>[22, 27]</Fire>
<Air>[24, 29]</Air>
Powders applied: [<Water>I</Water>, <Fire>I</Fire>, <Water>I</Water>]
Aggregation step:
<Water></Water>[3, 4] *2, 13% * 2 = {raw: [6, 8], conversion: .26}
<Fire></Fire>[2, 5], 14% * 1 = {raw: [2, 5], conversion: .14}
Application step:
W conversion: [89, 108] * .26 = [23.14, 28.08]
<Water></Water>= [41, 50] + [23.14, 28.08] + [6, 8] = <Water>[70.16, 86.08]</Water>
<Neutral></Neutral> = [89, 108] - [23.14, 28.08] = <Neutral>[65.86, 79.92]</Neutral>
F conversion: [65.86, 79.92] * .14 = [9.2204, 11.1888]
<Fire></Fire> = [22, 27] + [9.2204, 11.1888] + [2, 5] = <Fire>[33.2204, 43.1888]</Fire>
<Neutral></Neutral> = [65.86, 79.92] - [9.2204, 11.1888] = <Neutral>[56.6396, 68.7312]</Neutral>
Final (displayed) ranges
floor(<Neutral>[56.6396, 68.7312]</Neutral>) = <Neutral>[56, 68]</Neutral>
floor(<Water>[70.16, 86.08]</Water>) = <Water>[70, 86]</Water>
floor(<Fire>[33.2204, 43.1888]</Fire>)= <Fire>[33, 43]</Fire>
floor(<Air>[24, 29]</Air>) = <Air>[24, 29]</Air></pre>
<p>And, to compare, here's the crafted weapon after applying powders. Again, the numbers are slightly off for the same reasons as they are with the regular weapon example.</p>
<div class="text-center fig">
<img src="./media/CR_postpowder.png" width = "500"/>
<div class = "caption">The crafted weapon after applied powders.</div>
</div>
<p>We did the same melee attack testing on combat dummies. We leave damage calculation out of this example, but the damages observed more closely match the calculated numbers than the displayed numbers. Here's a screenshot:</p>
<div class="text-center fig">
<img src="./media/CR_attack.png" width = "500"/>
</div>
</div>
</span>
</div>
<div class="section">
</div>
</main>
<script type="text/javascript" src="/thirdparty/katex.js"></script>
<script type="text/javascript" src="/js/icons.js"></script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 722 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 KiB

View file

@ -1,172 +0,0 @@
// ['Title', ["type of paper","file name"]]
const pdfs = new Map([
["Wynncraft Damage Calculation",
["Mechanics", "Damage_calculation", "hppeng, ferricles, et al.", "A complete guide to Wynncraft's damage calculations. Includes formulas, tested game values, and worked examples."
]],
["Crafted Weapon Powder Mechanics",
["Mechanics", "Crafted_Weapon_Powder_Mechanics", "ferricles", "A short guide to the mechanics of powder application on crafted weapons. Includes formulas and a worked example."
]],
["Spell Costs",
["Mechanics", "Spell_Costs", "Bart MC, ferricles", "A documentation of spell costs and the mechanics of spell cost reduction."
]],
//[title ,[genre, filename, author(s), abstract/desc]]
]);
const changelog = new Map([
["Build Version 6 (20 May 2022)",
[
" + Added Tomes",
" + Changed Build encode and decode schemes to account for tomes",
]
],
["WynnBuilder^2 (12 May 2022)",
[
" + Switched most of Wynnbuilder over to Bootstrap",
" - Old UI",
]
],
//[title ,[genre, filename, author(s), abstract/desc]]
]);
const sections = ["Changelog", "Mechanics", "History" ]
function init() {
initSections();
for ([title, pdf] of pdfs) {
let sec = document.getElementById(pdf[0]+"-section");
if (sec) {
let pre = document.createElement("pre");
let firstline = document.createElement("div");
firstline.style.display = "flex";
firstline.style.justifyContent = "space-between";
let titleElem = document.createElement("p");
titleElem.textContent = title;
let a = document.createElement("a");
a.href = "./pdfs/" + pdf[1] + ".pdf";
a.target = "_blank";
a.textContent = pdf[1] + ".pdf";
a.classList.add("link");
a.style.display = "flex-end";
let secondline = document.createElement("div");
secondline.style.display = "flex";
let ul = document.createElement("ul");
ul.style.wordBreak = "break-word";
let li = document.createElement("li");
let div = document.createElement("div");
if (pdf[2]) {
li.textContent = "Author(s): " + pdf[2];
} else {
li.textContent = "Author(s): Unknown";
}
ul.appendChild(li);
li = document.createElement("li");
div = document.createElement("div");
if (pdf[3]) {
div.textContent = "Description: " + pdf[3];
} else {
div.textContent = "Description: None";
}
ul.appendChild(li);
li.appendChild(div);
pre.appendChild(firstline);
firstline.appendChild(titleElem);
firstline.appendChild(a);
pre.appendChild(secondline);
secondline.appendChild(ul);
sec.appendChild(document.createElement("br"));
sec.appendChild(pre);
} else {
console.log("Invalid paper type for " + title + ": " + pdf[0]);
}
}
let sec = document.getElementById("Changelog-section");
for ([version, changes] of changelog) {
let pre = document.createElement("pre");
let firstline = document.createElement("div");
firstline.style.display = "flex";
firstline.style.justifyContent = "space-between";
let titleElem = document.createElement("p");
titleElem.textContent = "Version " + version;
pre.appendChild(firstline);
firstline.appendChild(titleElem);
sec.appendChild(document.createElement("br"));
sec.appendChild(pre);
let ul = document.createElement("ul");
ul.style.listStyle = "none";
for (change of changes) {
let li = document.createElement("li");
li.textContent = change;
if (change.substring(0,3) === " + ") {
li.classList.add("positive");
} else if (change.substring(0,3) === " - ") {
li.classList.add("negative");
} else {
}
ul.appendChild(li);
}
pre.appendChild(ul);
}
}
function initSections() {
let main = document.getElementById("main");
for (const sec of sections) {
let div = document.createElement("div");
div.classList.add("row", "my-2");
div.id = sec;
let secspan = document.createElement("span");
secspan.classList.add("row", "up", "clickable");
div.appendChild(secspan);
let title = document.createElement("div");
title.classList.add("col-10", "item-title", "text-start")
title.style.margin = "0 0 0";
title.textContent = "Section: " + sec;
let indicator = document.createElement("div");
indicator.classList.add("col-auto", "fw-bold", "box-title");
indicator.textContent = "V";
secspan.appendChild(title);
secspan.appendChild(indicator);
let section = document.createElement("section");
section.classList.add("toggle-section");
section.id = sec + "-section";
section.style.display = "none";
div.appendChild(section);
main.appendChild(div);
secspan.addEventListener("click", function(){
if (secspan.classList.contains("up")) {
secspan.classList.remove("up");
secspan.classList.add("down");
indicator.style.transform = 'rotate(180deg)';
section.style.display = "";
} else {
secspan.classList.remove("down");
secspan.classList.add("up");
indicator.style.transform = 'rotate(0deg)';
section.style.display = "none";
}
});
}
}
init();

View file

@ -1,354 +0,0 @@
.header {
text-align: center;
display: flex;
flex-direction: row;
justify-content: center;
}
.title{
text-align: center;
font-size: 150%;
}
.smalltitle{
text-align: center;
font-size: 125%;
margin-top: 10px;
margin-bottom: 4px;
}
.headericon {
/* JANK FIX IF CAN */
max-height: 48px;
max-width: 48px;
}
.headerleft {
display: inline-block;
width: 35%;
text-align: left;
}
.headercenter {
display: inline-block;
width: 30%;
text-align: center;
}
.headerright{
display: inline-block;
width: 35%;
text-align: right;
}
.iconlink {
position: relative;
left: 0%;
right: 0%;
height: 100%;
width: 100%;
}
.all {
font-family: 'Nunito',sans-serif;
font-weight: 700;
}
.left{
margin: 2px 2%;
text-align: left;
}
.center {
text-align: center;
position: relative;
}
.itemp, .damagep {
margin: 2px 2%;
padding: 0;
}
/*Scrollbar*/
/* width */
::-webkit-scrollbar {
width: 10px;
}
/* Track */
::-webkit-scrollbar-track {
box-shadow: inset 0 0 5px #BCBCBC;
border: #BCBCBC;
border-radius: 5px;
}
/* Handle */
::-webkit-scrollbar-thumb {
background: #aaa;
border-radius: 10px;
}
/* Ugly Corner */
::-webkit-scrollbar-corner{
background: #110110;
}
.tooltip {
position: relative;
}
.tooltip .tooltiptext {
visibility: hidden;
color: #ddd;
background: #110110;
/*width: min(200%, 75vw);*/
display: inline-block;
padding: 0 min(2%,10px) 0 min(2%,10px);
text-align: center;
border: 1px solid #BCBCBC;
border-radius: 2px;
position: absolute;
z-index: 1;
}
.tooltip:hover .tooltiptext {
visibility: visible;
}
.header-tooltip {
top: 100%;
left: -20%;
}
.button-narrow {
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: 90%;
display: inline-block;
}
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;
}
button:hover {
background-color: #556;
}
button:active {
background-color: #558;
box-shadow: 0 3px #666;
transform: translateY(1px);
}
/*==============================================================*/
.all{
background: #121516;
color: #aaa;
}
a.link{
color: #A5FDFF;
}
main {
margin: 24px 0 48px;
}
main h2 {
margin: 0 0 8px;
color: #bbb;
font-size: 24pt;
font-weight: bold;
}
main p {
margin: 0 0 8px;
line-height: 1.35em;
text-indent: 2.5em;
text-align: justify;
font-size: 13pt;
font-weight: 700;
}
main .footer {
font-size: 10pt;
text-align: center;
}
main code {
padding: 0 3px;
background-color: #1d1f21;
color: #de935f;
font-family: 'Source Code Pro', 'Ubuntu Mono', 'Courier New', monospace;
}
main pre { /* tomorrow night: https://github.com/chriskempson/tomorrow-theme */
margin: 14px 2px;
padding: 10px 16px;
border-left: 4px solid #7f7f7f;
background-color: #1d1f21;
color: #ddd;
font-size: 12pt;
font-weight: normal;
}
main pre > .prop {
color: #cc6666;
}
main pre > .fn {
color: #f0c674;
}
main pre > .bool {
color: #b294bb;
}
main pre > .str {
color: #b5bd68;
}
main pre > .num {
color: #81a2be;
}
main pre > .op {
color: #ccc;
}
main strong {
color: #ccc;
font-weight: bold;
}
main .rb-text {
background-image: linear-gradient(to left, #f5f, #a0a, #5ff, #5f5, #ff5, #fa0, #a00, #f5f);
background-size: 300% 100%;
-webkit-background-clip: text;
color: transparent;
animation: scroll-bg 4s linear infinite;
}
main .math {
font-family: 'CMU Serif', 'Cambria Math', 'Times New Roman', serif;
}
main .heart {
color: #e44078;
}
@keyframes scroll-bg {
0% {
background-position-x: 0;
}
100% {
background-position-x: 300%;
}
}
.full-width {
width: 100%;
margin: 4px 0 32px;
padding: 0;
}
.full-width > img {
width: 100%;
background-color: #000;
filter: brightness(0.7) contrast(1.2) grayscale(0.5);
}
.section {
margin: 0 10vw 28px;
}
.docs {
margin: 14px 0;
}
.docs h3 {
margin: 0 0 8px;
color: #aaa;
font-size: 20pt;
font-weight: bold;
}
.docs-entry {
margin: 4px 0 16px;
}
.docs-entry-key {
margin-bottom: 6px;
padding: 4px 6px;
display: flex;
flex-flow: row;
align-items: baseline;
background-color: #1d1f21;
font-weight: normal;
}
.docs-entry-key-id {
color: #cc6666;
font-size: 16pt;
font-family: 'Source Code Pro', 'Ubuntu Mono', 'Courier New', monospace;
}
.docs-entry-key-type {
flex: 1;
margin-left: 8px;
color: #de935f;
font-size: 11pt;
font-family: 'Source Code Pro', 'Ubuntu Mono', 'Courier New', monospace;
}
.docs-entry-key-alias {
float: right;
color: #515356;
font-size: 11pt;
}
.docs-entry-key-alias > .alias {
float: none;
color: #969896;
font-family: 'Source Code Pro', 'Ubuntu Mono', 'Courier New', monospace;
}
.docs-entry > p {
margin-left: 1.5em;
text-indent: 0;
}
.indicator-img {
max-height: 24px;
top: 0;
bottom: 0;
display: flex-end;
margin: auto 0 auto;
}
.span-flex > * {
display: inline-block;
}
.span-flex {
width: 100%;
display: flex;
justify-content: space-between;
}
.span-flex:hover {
text-decoration: underline;
}
ul{
word-wrap: break-word;
word-break: break-word;
white-space: pre-wrap;
}
.positive {
color: #5f5;
/*text-shadow: 2px 2px 0 #153f15;*/
}
.negative {
color: #f55;
/*text-shadow: 2px 2px 0 #1f1515;*/
}