* 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>
This commit is contained in:
hppeng-wynn 2023-12-29 18:33:04 -08:00 committed by GitHub
parent 165adf6dcc
commit df9412e994
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 322169 additions and 117246 deletions

180857
clean.json

File diff suppressed because it is too large Load diff

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

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

@ -33,7 +33,8 @@ const wynn_version_names = [
'2.0.2.1', '2.0.2.1',
'2.0.2.3', '2.0.2.3',
'2.0.3.1', '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; const WYNN_VERSION_LATEST = wynn_version_names.length - 1;
// Default to the newest version. // Default to the newest version.

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": { "MAGNET": {
"displayName": "Magnet", "displayName": "Magnet",
"description": "Pulls items within an 8 block radius towards you", "description": "Pulls items within an 8 block radius towards you",
@ -214,7 +224,7 @@
"abilities": [] "abilities": []
}, },
"DESC_FESTIVESPIRIT": { "DESC_FESTIVESPIRIT": {
"displayName": "Festive Spirits", "displayName": "Festive Spirit",
"description": "Plays wintery tunes", "description": "Plays wintery tunes",
"abilities": [] "abilities": []
}, },
@ -260,7 +270,7 @@
}] }]
}, },
"ALTEREGO": { "ALTEREGO": {
"displayName": "Alterego", "displayName": "Alter Ego",
"description": "Awakened can be activated after saving 40% less mana, but its duration is reduced by 25%.", "description": "Awakened can be activated after saving 40% less mana, but its duration is reduced by 25%.",
"abilities": [] "abilities": []
}, },
@ -296,7 +306,7 @@
}] }]
}, },
"SOUL_EATER": { "SOUL_EATER": {
"displayName": "Soul eater", "displayName": "Soul Eater",
"description": "Devour and Harvester grant double mana, but your maximum Marks are decreased by 1.", "description": "Devour and Harvester grant double mana, but your maximum Marks are decreased by 1.",
"abilities": [{ "abilities": [{
"class": "Assassin", "class": "Assassin",
@ -508,5 +518,15 @@
} }
] ]
}] }]
},
"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",] 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) { function encodeCraft(craft) {

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 // @See https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/video-store/index.jsA
let db; let db;

View file

@ -1,4 +1,4 @@
const ING_DB_VERSION = 29; const ING_DB_VERSION = 30;
// @See https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/video-store/index.js // @See https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/video-store/index.js

View file

@ -3847,5 +3847,26 @@
"Pain Cycle": 3845, "Pain Cycle": 3845,
"Psionic Pretense": 3846, "Psionic Pretense": 3846,
"Propeller Hat": 3847, "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 = { import json
#"name": "name", with open("translate_mappings.json", 'r') as infile:
#"displayName": "displayName", translate_mappings = json.load(infile)
#"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",
}
delete_keys = [ delete_keys = [
#"addedLore", #"addedLore",
@ -143,3 +9,36 @@ delete_keys = [
#"armorColor", #"armorColor",
#"material" #"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 "notTouching": 0
} }
if 'itemIDs' not in ing: if 'itemIDs' not in ing:
ing['consumableIDs'] = { ing['itemIDs'] = {
"dura": 0, "dura": 0,
"strReq": 0, "strReq": 0,
"dexReq": 0, "dexReq": 0,

View file

@ -15,15 +15,16 @@ import sys
import os import os
import base64 import base64
import argparse 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 = 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') parser.add_argument('outfile', help='output file to dump clean data into')
args = parser.parse_args() 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()) data = json.loads(in_file.read())
@ -65,8 +66,9 @@ for item in items:
for k, v in translate_mappings.items(): for k, v in translate_mappings.items():
if k in item: if k in item:
item[v] = item[k] tmp = item[k]
del item[k] del item[k]
item[v] = tmp
if not (item["name"] in id_map): if not (item["name"] in id_map):
while max_id in used_ids: 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,230 @@
"""
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 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=(',', ':'))