Exemple #1
0
def extend_decoration_chances(decoration_map: DataMap):
    """Calculates the drop tables given the decoration map.

    Each decoration is part of a drop table (decided by rarity), and feystones
    will individually land on a drop table. Once on a drop table, each decoration in that drop table
    has an "equal" chance within that drop table.

    Odds are listed here, with one typo (gleaming is actually glowing).
    https://docs.google.com/spreadsheets/d/1ysj6c2boC6GarFvMah34e6VviZeaoKB6QWovWLSGlsY/htmlview?usp=sharing&sle=true#
    """

    rarity_to_table = {
        5: 'C',
        6: 'B',
        7: 'A',
        8: 'S'
    }

    jewel_to_table_odds = {
        'mysterious': { 'C': 85, 'B': 15, 'A': 0,  'S': 0 },
        'glowing':    { 'C': 65, 'B': 34, 'A': 1,  'S': 0 },
        'worn':       { 'C': 10, 'B': 82, 'A': 6,  'S': 2 },
        'warped':     { 'C': 0,  'B': 77, 'A': 18, 'S': 5 },
    }

    drop_tables = rarity_to_table.values()
    
    # Calculate how many entries there are per drop table type
    table_counts = { table:0 for table in drop_tables }
    for entry in decoration_map.values():
        table = rarity_to_table[entry['rarity']]
        table_counts[table] += 1

    # Create an odds map for each drop table level
    # This maps droptable -> feystone -> probability
    # This is necessary because all decorations are assigned to a droptable
    odds_map = { }
    for table in drop_tables:
        odds_map[table] = {}
        for feystone, feystone_odds in jewel_to_table_odds.items():
            value = Decimal(feystone_odds[table]) / Decimal(table_counts[table])
            odds_map[table][feystone] = value.quantize(Decimal('1.00000'))

    # Assign the odds map for the drop table level to the decoration itself
    for entry in decoration_map.values():
        table_name = rarity_to_table[entry['rarity']]
        entry['chances'] = odds_map[table_name]
Exemple #2
0
def test_can_iterate_values_in_order():
    expected_names = ['test1', 'test2', 'test3']

    map = DataMap()
    for (id, name) in enumerate(expected_names):
        map.add_entry(id, create_test_entry_en(name))

    found = [entry['name']['en'] for entry in map.values()]
    assert found == expected_names, "Expected map entries to match"
Exemple #3
0
def extend_decoration_chances(decoration_map: DataMap):
    """Calculates the drop tables given the decoration map.

    Each decoration is part of a drop table (decided by rarity), and feystones
    will individually land on a drop table. Once on a drop table, each decoration in that drop table
    has an "equal" chance within that drop table.

    Odds are listed here, with one typo (gleaming is actually glowing).
    https://docs.google.com/spreadsheets/d/1ysj6c2boC6GarFvMah34e6VviZeaoKB6QWovWLSGlsY/htmlview?usp=sharing&sle=true#
    """

    jewel_to_table_odds = {}
    droprates = read_csv(
        join(data_path, "decorations/decoration_droprates.csv"))
    for row in droprates:
        entries = {}
        for i in range(5, 14):
            entries[i] = int(row[str(i)] or '0')
        jewel_to_table_odds[row['feystone']] = entries

    # Calculate how many entries there are per drop table type
    table_counts = {table: 0 for table in range(5, 14)}
    for entry in decoration_map.values():
        table_counts[entry['rarity']] += 1

    # Create an odds map for each drop table level
    # This maps droptable -> feystone -> probability
    # This is necessary because all decorations are assigned to a droptable
    odds_map = {}
    for table in range(5, 14):
        odds_map[table] = {}
        for feystone, feystone_odds in jewel_to_table_odds.items():
            count = table_counts[table]
            if count == 0:
                continue
            value = Decimal(feystone_odds[table]) / Decimal(count)
            odds_map[table][feystone] = value.quantize(Decimal('1.00000'))

    # Assign the odds map for the drop table level to the decoration itself
    for entry in decoration_map.values():
        entry['chances'] = odds_map[entry['rarity']]
Exemple #4
0
def copy_skill_descriptions(skill_map: DataMap):
    """Copies the descriptions of certain skill levels to the skill tree.

    Some skill trees are "artificial" and do not exist in the game, therefore they
    have no actual description. This includes skills like Good Luck. Therefore,
    should certain conditions be applied, we reuse the skill detail description.

    The conditions for it to occur are:
    - Missing an english description (missing a translation shouldn't trigger this)
    - Only one available skill level (multi-stage skills are ignored)
    """

    for tree_entry in skill_map.values():
        if tree_entry['description']['en']:
            continue
        if len(tree_entry['levels']) != 1:
            continue
        
        # We don't do a default translation here, since its handled by another part of the build
        level_entry = tree_entry['levels'][0]
        for language in cfg.supported_languages:
            tree_entry['description'][language] = level_entry['description'][language]
Exemple #5
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)