Exemplo n.º 1
0
class ItemTextHandler():
    "A class that loads item text and tracks encountered items"

    def __init__(self):
        self._item_text = load_text("common/text/steam/item")
        self.encountered = OrderedSet()

    def name_for(self, item_id: int):
        self.encountered.add(item_id)
        return self._item_text[item_id * 2]

    def description_for(self, item_id: int):
        self.encountered.add(item_id)
        return self._item_text[item_id * 2 + 1]

    def text_for(self, item_id: int):
        self.encountered.add(item_id)
        return (self._item_text[item_id * 2], self._item_text[item_id * 2 + 1])
Exemplo n.º 2
0
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.
    # Unk7 is max level pre-augment, Unk8 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.unk7 - 1, entry.unk8 - 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()
    item_text_handler = ItemTextHandler()
    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:
                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 track the encounter.
            recipe_binary = armor_data.recipe
            new_data['craft'] = convert_recipe(item_text_handler,
                                               recipe_binary)

            # 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")

    item_updater.add_missing_items(item_text_handler.encountered)
Exemplo n.º 3
0
def update_items(item_updater: ItemUpdater, *, mhdata=None):
    if not mhdata:
        mhdata = load_data()
        print("Existing Data loaded. Using to expand item list")

    new_item_map = DataMap(languages='en', start_id=mhdata.item_map.max_id + 1)
    unlinked_item_names = OrderedSet()

    # used to track dupes to throw proper errors
    updated_names = set()

    # First pass. Iterate over existing ingame items and merge with existing data
    for entry in item_updater.item_data:
        name_dict, description_dict = item_updater.name_and_description_for(
            entry.id, track=False)
        existing_item = mhdata.item_map.entry_of('en', name_dict['en'])

        is_encountered = entry.id in item_updater.encountered_item_ids
        if not is_encountered and not existing_item:
            unlinked_item_names.add(name_dict['en'])
            continue

        if name_dict['en'] in updated_names:
            raise Exception(f"Duplicate item {name_dict['en']}")
        updated_names.add(name_dict['en'])

        # note: we omit buy price as items may have a buy price even if not sold.
        # We only care about the buy price of BUYABLE items
        new_data = {
            'name': name_dict,
            'description': description_dict,
            'rarity': entry.rarity + 1,
            'sell_price': None,
            'points': None
        }

        is_ez = entry.flags.ez
        is_account = entry.type == 'endemic'
        is_tradein = "(Trade-in Item)" in description_dict['en']
        is_appraisal = entry.flags.appraisal

        sell_value = entry.sell_price if entry.sell_price != 0 else None
        if is_account:
            new_data['points'] = sell_value
        else:
            new_data['sell_price'] = sell_value

        if name_dict['en'] == 'Normal Ammo 1':
            new_data['category'] = 'hidden'
        elif is_ez:
            new_data['category'] = 'misc'
            new_data['subcategory'] = 'trade' if is_tradein else 'supply'
        elif is_account:
            new_data['category'] = 'misc'
            new_data['subcategory'] = 'trade' if is_tradein else 'account'
        elif is_appraisal or ('Appraised after investigation'
                              in description_dict['en']):
            new_data['category'] = 'misc'
            new_data['subcategory'] = 'appraisal'
            new_data['sell_price'] = None  # why does this have values?
        else:
            new_data['category'] = entry.type
            new_data['subcategory'] = 'trade' if is_tradein else None

            # Whether we show carry limit at all is based on item type.
            # Materials are basically infinite carry
            infinite_carry = new_data['category'] == 'material'
            new_data[
                'carry_limit'] = None if infinite_carry else entry.carry_limit

        if existing_item:
            new_item_map.insert({**existing_item, **new_data})
        else:
            new_item_map.insert(new_data)

    # Second pass, add old entries that are not in the new one
    for old_entry in mhdata.item_map.values():
        if old_entry.name('en') not in new_item_map.names('en'):
            new_item_map.insert(old_entry)

    # Third pass. Items need to be reordered based on type

    unsorted_item_map = new_item_map  # store reference to former map

    def filter_category(category, subcategory=None):
        "helper that returns items and then removes from unsorted item map"
        results = []
        for item in unsorted_item_map.values():
            if item['category'] == category and item[
                    'subcategory'] == subcategory:
                results.append(item)
        for result in results:
            del unsorted_item_map[result.id]
        return results

    normal_ammo_1 = unsorted_item_map.entry_of("en", "Normal Ammo 1")

    # start the before-mentioned third pass by creating a new map based off the old one
    new_item_map = DataMap(languages="en")
    new_item_map.extend(filter_category('item'))
    new_item_map.extend(filter_category('material'))
    new_item_map.extend(filter_category('material', 'trade'))
    if normal_ammo_1:
        new_item_map.insert(normal_ammo_1)
    new_item_map.extend(filter_category('ammo'))
    new_item_map.extend(filter_category('misc', 'appraisal'))
    new_item_map.extend(filter_category('misc', 'account'))
    new_item_map.extend(filter_category('misc', 'supply'))

    # Write out data
    writer = create_writer()

    writer.save_base_map_csv(
        "items/item_base.csv",
        new_item_map,
        schema=schema.ItemSchema(),
        translation_filename="items/item_base_translations.csv",
        translation_extra=['description'])

    # Write out artifact data
    print("Writing unlinked item names to artifacts")
    artifacts.write_names_artifact('items_unlinked.txt', unlinked_item_names)
    print("Writing all items and ids")
    artifact_data = [{
        'id': i.id,
        'name': i.name['en']
    } for i in item_updater.data]
    artifacts.write_dicts_artifact('items_ids.csv', artifact_data)

    print("Item files updated")
Exemplo n.º 4
0
def update_armor():
    "Populates and updates armor information using the armorset_base as a source of truth"

    armor_text = load_text("common/text/steam/armor")
    armorset_text = load_text("common/text/steam/armor_series")

    # Parses binary armor data, mapped by the english name
    armor_data = {}
    for armor_entry in load_schema(am_dat.AmDat,
                                   "common/equip/armor.am_dat").entries:
        if armor_entry.gender == 0: continue
        if armor_entry.order == 0: continue
        name_en = armor_text[armor_entry.gmd_name_index]['en']
        armor_data[name_en] = armor_entry

    # Parses craft data, mapped by the binary armor id
    armor_craft_data = {}
    for craft_entry in load_schema(eq_crt.EqCrt,
                                   "common/equip/armor.eq_crt").entries:
        armor_craft_data[craft_entry.equip_id] = craft_entry

    # Get number of times armor can be upgraded by rarity level.
    # Unk7 is max level pre-augment, Unk8 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.unk7 - 1, entry.unk8 - 1)

    print("Binary data loaded")

    mhdata = load_data()
    print(
        "Existing Data loaded. Using existing armorset data to drive new armor data."
    )

    # Will store results. Language lookup and validation will be in english
    new_armor_map = DataMap(languages="en")
    new_armorset_bonus_map = DataMap(languages="en")

    # Temporary storage for later processes
    all_set_skill_ids = OrderedSet()

    item_text_handler = ItemTextHandler()
    skill_text_handler = SkillTextHandler()

    print("Populating armor data, keyed by the armorset data")
    next_armor_id = mhdata.armor_map.max_id + 1
    for armorset in mhdata.armorset_map.values():
        # Handle armor pieces
        for part, armor_name in datafn.iter_armorset_pieces(armorset):
            existing_armor = mhdata.armor_map.entry_of('en', armor_name)
            armor_binary = armor_data.get(armor_name)

            if not armor_binary:
                raise Exception(
                    f"Failed to find binary armor data for {armor_name}")

            if armor_binary.set_skill1_lvl > 0:
                all_set_skill_ids.add(armor_binary.set_skill1)

            rarity = armor_binary.rarity + 1
            name_dict = armor_text[armor_binary.gmd_name_index]

            # Initial new armor data
            new_data = {
                'name':
                name_dict,  # Override for translation support!
                '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': {}
            }

            # 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 track the encounter.
            recipe_binary = armor_craft_data[armor_binary.id]
            new_data['craft'] = convert_recipe(item_text_handler,
                                               recipe_binary)

            armor_entry = None
            if not existing_armor:
                print(
                    f"Entry for {armor_name} not in armor map, creating new entry"
                )
                armor_entry = new_armor_map.add_entry(next_armor_id, new_data)
                next_armor_id += 1

            else:
                armor_entry = new_armor_map.add_entry(existing_armor.id, {
                    **existing_armor,
                    **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/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")

    add_missing_items(item_text_handler.encountered, mhdata=mhdata)