def update_decorations(mhdata, item_data: ItemCollection): print("Updating decorations") data = DecorationCollection(item_data) skill_text_handler = SkillTextHandler() # write artifact file (used to debug) def create_deco_artifact(d): return {'name': d.name['en'], 'slot': d.size, 'rarity': d.rarity} artifacts.write_dicts_artifact( "decorations_all.csv", list(map(create_deco_artifact, data.decorations))) for entry in mhdata.decoration_map.values(): deco_name = entry['name_en'] try: deco = data.by_name(entry['name_en']) except KeyError: print(f"Could not find decoration {deco_name} in the game files") continue entry['name'] = deco.name entry['rarity'] = deco.rarity for i in range(2): skill_name = None skill_pts = None if i < len(deco.skills): (skill_id, skill_pts) = deco.skills[i] skill_name = skill_text_handler.get_skilltree_name( skill_id)['en'] entry[f'skill{i+1}_name'] = skill_name entry[f'skill{i+1}_level'] = skill_pts writer = create_writer() writer.save_base_map_csv( "decorations/decoration_base.csv", mhdata.decoration_map, schema=schema.DecorationBaseSchema(), translation_filename="decorations/decoration_base_translations.csv") print("Decoration files updated\n")
def update_armor(mhdata, item_updater: ItemUpdater): "Populates and updates armor information using the armorset_base as a source of truth" armor_series = load_armor_series() # Get number of times armor can be upgraded by rarity level. # The first is max level pre-augment, the second is max post-augment # Thanks to the MHWorld Modders for the above info rarity_upgrades = {} for entry in load_schema(arm_up.ArmUp, "common/equip/arm_upgrade.arm_up").entries: rarity_upgrades[entry.index + 1] = (entry.unk10 - 1, entry.unk11 - 1) print("Binary armor data loaded") print("Writing list of armorset names (in order) to artifacts") artifacts.write_names_artifact( 'setnames.txt', [s.name['en'] for s in armor_series.values()]) # Will store results. Language lookup and validation will be in english new_armorset_map = DataMap(languages="en", start_id=mhdata.armorset_map.max_id + 1) new_armor_map = DataMap(languages="en", start_id=mhdata.armor_map.max_id + 1) new_armorset_bonus_map = DataMap(languages="en") # Temporary storage for later processes all_set_skill_ids = OrderedSet() skill_text_handler = SkillTextHandler() armor_data_by_name = {} print( "Updating set data, keyed by the existing names in armorset_base.csv") for armorset_entry in mhdata.armorset_map.values(): armorseries_data = armor_series.get(armorset_entry.name('en')) if not armorseries_data: print( f"Armor series {armorset_entry.name('en')} doesn't exist in binary, skipping" ) new_armorset_map.insert(armorset_entry) continue new_entry = { **armorset_entry, 'name': armorseries_data.name, 'rank': armorseries_data.rank } first_armor = armorseries_data.armors[0].binary if first_armor.set_skill1_lvl > 0: skill_id = first_armor.set_skill1 all_set_skill_ids.add(skill_id) new_entry['bonus'] = skill_text_handler.get_skilltree_name( skill_id)['en'] for part in cfg.armor_parts: armor = armorseries_data.get_part(part) if armor: armor_data_by_name[armor.name['en']] = armor new_entry[part] = armor.name['en'] else: new_entry[part] = None new_armorset_map.insert(new_entry) print("Armorset entries updated") print("Updating armor") for armorset_entry in new_armorset_map.values(): # Handle armor pieces for part, armor_name in datafn.iter_armorset_pieces(armorset_entry): existing_armor = mhdata.armor_map.entry_of('en', armor_name) armor_data = armor_data_by_name.get(armor_name, None) if not armor_data: if not existing_armor: print( f"Error: Invalid entry {armor_name}, does not exist in current DB nor is it in armor data" ) continue print( f"Failed to find binary armor data for {armor_name}, maintaining existing data" ) new_armor_map.insert(existing_armor) continue armor_binary = armor_data.binary rarity = armor_binary.rarity + 1 # Initial new armor data new_data = { 'name': armor_data.name, 'rarity': rarity, 'type': part, 'gender': gender_list[armor_binary.gender], 'slot_1': armor_binary.gem_slot1_lvl, 'slot_2': armor_binary.gem_slot2_lvl, 'slot_3': armor_binary.gem_slot3_lvl, 'defense_base': armor_binary.defense, 'defense_max': armor_binary.defense + rarity_upgrades[rarity][0] * 2, 'defense_augment_max': armor_binary.defense + rarity_upgrades[rarity][1] * 2, 'defense_fire': armor_binary.fire_res, 'defense_water': armor_binary.water_res, 'defense_thunder': armor_binary.thunder_res, 'defense_ice': armor_binary.ice_res, 'defense_dragon': armor_binary.dragon_res, 'skills': {}, 'craft': {} } if existing_armor: new_data['id'] = existing_armor.id # Add skills to new armor data for i in range(1, 2 + 1): skill_lvl = getattr(armor_binary, f"skill{i}_lvl") if skill_lvl != 0: skill_id = getattr(armor_binary, f"skill{i}") name_en = skill_text_handler.get_skilltree_name( skill_id)['en'] new_data['skills'][f'skill{i}_name'] = name_en new_data['skills'][f'skill{i}_pts'] = skill_lvl else: new_data['skills'][f'skill{i}_name'] = None new_data['skills'][f'skill{i}_pts'] = None # Add recipe to new armor data. Also tracks the encounter. new_data['craft'] = convert_recipe(item_updater, armor_data.recipe) # Add new data to new armor map new_armor_map.insert(new_data) # Process set skills. As we don't currently understand the set -> skill map, we only translate # We pull the already established set skill name from existing CSV for bonus_entry in mhdata.armorset_bonus_map.values(): skilltree = skill_text_handler.get_skilltree(bonus_entry.name('en')) name_dict = skill_text_handler.get_skilltree_name(skilltree.index) new_armorset_bonus_map.insert({**bonus_entry, 'name': name_dict}) # Write new data writer = create_writer() writer.save_base_map_csv( "armors/armorset_base.csv", new_armorset_map, schema=schema.ArmorSetSchema(), translation_filename="armors/armorset_base_translations.csv") writer.save_base_map_csv( "armors/armor_base.csv", new_armor_map, schema=schema.ArmorBaseSchema(), translation_filename="armors/armor_base_translations.csv") writer.save_data_csv("armors/armor_skills_ext.csv", new_armor_map, key="skills") writer.save_data_csv("armors/armor_craft_ext.csv", new_armor_map, key="craft") writer.save_base_map_csv( "armors/armorset_bonus_base.csv", new_armorset_bonus_map, schema=schema.ArmorSetBonus(), translation_filename="armors/armorset_bonus_base_translations.csv") print("Armor files updated\n")
def update_weapons(mhdata, item_updater: ItemUpdater): skill_text_handler = SkillTextHandler() print("Beginning load of binary weapon data") weapon_loader = WeaponDataLoader() notes_data = load_schema(wep_wsl.WepWsl, "common/equip/wep_whistle.wep_wsl") sharpness_reader = SharpnessDataReader() ammo_reader = WeaponAmmoLoader() coating_data = load_schema(bbtbl.Bbtbl, "common/equip/bottle_table.bbtbl") print("Loaded weapon binary data") def bind_weapon_blade_ext(weapon_type: str, existing_entry, weapon): binary: wp_dat.WpDatEntry = weapon.binary for key in ['kinsect_bonus', 'phial', 'phial_power', 'shelling', 'shelling_level', 'notes']: existing_entry[key] = None if weapon_type == cfg.CHARGE_BLADE: existing_entry['phial'] = cb_phials[binary.wep1_id] if weapon_type == cfg.SWITCH_AXE: try: (phial, power) = s_axe_phials[binary.wep1_id] existing_entry['phial'] = phial existing_entry['phial_power'] = power except: raise KeyError(f"Failed to load saxe phials for {weapon.name['en']} (SAXE ID: {binary.wep1_id})") if weapon_type == cfg.GUNLANCE: # first 5 are normals, second 5 are wide, third 5 are long if binary.wep1_id >= 15: value = binary.wep1_id - 15 shelling = ['normal', 'wide', 'long'][value % 3] level = value // 3 + 6 else: shelling = ['normal', 'wide', 'long'][binary.wep1_id // 5] level = (binary.wep1_id % 5) + 1 existing_entry['shelling'] = shelling existing_entry['shelling_level'] = level if weapon_type == cfg.INSECT_GLAIVE: try: existing_entry['kinsect_bonus'] = glaive_boosts[binary.wep1_id] except: raise KeyError(f"Failed to load kinsect bonus for {weapon.name['en']} (BOOST ID: {binary.wep1_id})") if weapon_type == cfg.HUNTING_HORN: note_entry = notes_data[binary.wep1_id] notes = [note_entry.note1, note_entry.note2, note_entry.note3] notes = [str(note_colors[n]) for n in notes] existing_entry['notes'] = "".join(notes) # Load weapon tree binary data weapon_trees = {} for weapon_type in cfg.weapon_types: weapon_tree = weapon_loader.load_tree(weapon_type) print(f"Loaded {weapon_type} weapon tree binary data") weapon_trees[weapon_type] = weapon_tree # Load Kulve Augment Data kulve_augments = weapon_loader.load_kulve_augments() artifacts.write_dicts_artifact("kulve_augments.csv", kulve_augments.flattened()) # Write artifact lines print("Writing artifact files for weapons (use it to add new weapons)") write_weapon_artifacts(mhdata, weapon_trees, ammo_reader) # Store new weapon entries new_weapon_map = DataMap(languages=["en"], start_id=mhdata.weapon_map.max_id+1, keys_ex=["weapon_type"]) # Iterate over existing weapons, merge new data in for existing_entry in mhdata.weapon_map.values(): weapon_type = existing_entry['weapon_type'] weapon_tree = weapon_trees[weapon_type] # Note: weapon data ordering is unknown. order field and tree_id asc are sometimes wrong # Therefore its unsorted, we have to work off the spreadsheet order multiplier = cfg.weapon_multiplier[weapon_type] weapon = weapon_tree.by_name(existing_entry.name('en')) if not weapon: print(f"Could not find binary entry for {existing_entry.name('en')}") new_weapon_map.insert(existing_entry) continue is_kulve = existing_entry['category'] == 'Kulve' is_special = existing_entry['category'] in ('Kulve', 'Safi') binary = weapon.binary name = weapon.name new_entry = { **existing_entry } # Bind name and parent new_entry['name'] = name new_entry['weapon_type'] = weapon_type new_entry['previous_en'] = None if weapon.parent != None: new_entry['previous_en'] = weapon.parent.name['en'] # Apply augmentation if its a kulve weapon that can get augmented if is_kulve: augment_params = kulve_augments.get(weapon_type, weapon.rarity) if augment_params: weapon = AugmentedWeapon(weapon, augment_params, 4) # Bind info new_entry['weapon_type'] = weapon_type new_entry['rarity'] = weapon.rarity new_entry['attack'] = (weapon.attack * multiplier).quantize(Decimal('1.'), rounding=ROUND_HALF_UP) new_entry['affinity'] = weapon.affinity new_entry['defense'] = weapon.defense or None new_entry['slot_1'] = binary.gem_slot1_lvl new_entry['slot_2'] = binary.gem_slot2_lvl new_entry['slot_3'] = binary.gem_slot3_lvl new_entry['elderseal'] = elderseal[binary.elderseal] # Bind Elements if name['en'] in ["Twin Nails", "Fire and Ice", "Blizzard and Blaze"]: print(f"Skipping {name['en']} element data") else: hidden = binary.hidden_element_id != 0 element_atk = weapon.element_value new_entry['element_hidden'] = hidden new_entry['element1'] = weapon.element_type new_entry['element1_attack'] = element_atk * 10 if element_atk else None new_entry['element2'] = None new_entry['element2_attack'] = None # Bind skill skill = skill_text_handler.get_skilltree_name(binary.skill_id) new_entry['skill'] = skill['en'] if binary.skill_id != 0 else None # Bind Extras (Blade/Gun/Bow data) if weapon_type in cfg.weapon_types_melee: bind_weapon_blade_ext(weapon_type, new_entry, weapon) new_entry['sharpness'] = sharpness_reader.sharpness_for(binary) elif weapon_type in cfg.weapon_types_gun: tree = weapon.tree if is_special: tree = existing_entry['category'] (ammo_name, ammo_data) = ammo_reader.create_data_for( wtype=weapon_type, tree=tree, binary=weapon.binary) new_entry['ammo_config'] = ammo_name else: # TODO: Bows have an Enabled+ flag. Find out what it means # 1 = enabled, 2 = enabled+ coating_binary = coating_data[binary.special_ammo_type] new_entry['bow'] = { 'close': coating_binary.close_range > 0, 'power': coating_binary.power > 0, 'paralysis': coating_binary.paralysis > 0, 'poison': coating_binary.poison > 0, 'sleep': coating_binary.sleep > 0, 'blast': coating_binary.blast > 0 } # crafting data new_entry['craft'] = [] if weapon.craft: new_entry['craft'].append({ 'type': 'Create', **convert_recipe(item_updater, weapon.craft) }) if weapon.upgrade: new_entry['craft'].append({ 'type': 'Upgrade', **convert_recipe(item_updater, weapon.upgrade) }) new_weapon_map.insert(new_entry) # Write new data writer = create_writer() writer.save_base_map_csv( "weapons/weapon_base.csv", new_weapon_map, schema=schema.WeaponBaseSchema(), translation_filename="weapons/weapon_base_translations.csv" ) writer.save_data_csv( "weapons/weapon_sharpness.csv", new_weapon_map, key="sharpness", schema=schema.WeaponSharpnessSchema() ) writer.save_data_csv( "weapons/weapon_bow_ext.csv", new_weapon_map, key="bow", schema=schema.WeaponBowSchema() ) writer.save_data_csv( "weapons/weapon_craft.csv", new_weapon_map, key="craft", schema=schema.WeaponRecipeSchema() ) writer.save_keymap_csv( "weapons/weapon_ammo.csv", ammo_reader.data, schema=schema.WeaponAmmoSchema() ) print("Weapon files updated\n")
def update_charms(mhdata, item_updater: ItemUpdater, armor_collection: ArmorCollection): "Populates and updates charm information using the charm_base as a source of truth" print("Writing list of charm names (in order) to artifacts") def get_charm_raw(c): return { 'name_en': c.name['en'], 'parent': c.parent and c.parent.name['en'] } artifacts.write_dicts_artifact( 'charms_raw.csv', [get_charm_raw(c) for c in armor_collection.charms]) skill_text_handler = SkillTextHandler() charm_by_name = {c.name['en']: c for c in armor_collection.charms} new_charm_map = DataMap(languages=["en"]) for charm_entry in mhdata.charm_map.values(): new_charm_entry = {**charm_entry} data = charm_by_name.get(charm_entry['name_en']) if not data: print( f"Warning: Charm {charm_entry['name_en']} has no associated binary data" ) new_charm_map.insert(new_charm_entry) continue new_charm_entry['name'] = data.name new_charm_entry['previous_en'] = data.parent and data.parent.name['en'] new_charm_entry['rarity'] = data.rarity # Add skills to new armor data skills = data.skills + ([(None, None)] * (2 - len(data.skills))) for i, (skill_id, skill_lvl) in enumerate(skills): if skill_id is None: new_charm_entry[f'skill{i+1}_name'] = None new_charm_entry[f'skill{i+1}_level'] = None else: name_en = skill_text_handler.get_skilltree_name(skill_id)['en'] new_charm_entry[f'skill{i+1}_name'] = name_en new_charm_entry[f'skill{i+1}_level'] = skill_lvl new_charm_entry['craft'] = [] recipes = [('Create', data.craft), ('Upgrade', data.upgrade)] for rtype, recipe in recipes: if recipe: new_charm_entry['craft'].append({ 'type': rtype, **convert_recipe(item_updater, recipe) }) new_charm_map.insert(new_charm_entry) # Write new data writer = create_writer() writer.save_base_map_csv( 'charms/charm_base.csv', new_charm_map, translation_filename="charms/charm_base_translations.csv", schema=schema.CharmBaseSchema()) writer.save_data_csv("charms/charm_craft.csv", new_charm_map, key="craft") print("Charm files updated\n")