"""
Option definitions for Pokemon Emerald
"""
from dataclasses import dataclass

from Options import (Choice, DeathLink, DefaultOnToggle, OptionSet, NamedRange, Range, Toggle, FreeText,
                     PerGameCommonOptions, OptionGroup, StartInventory)

from .data import data


class Goal(Choice):
    """
    Determines what your goal is to consider the game beaten.

    - Champion: Become the champion and enter the hall of fame
    - Steven: Defeat Steven in Meteor Falls
    - Norman: Defeat Norman in Petalburg Gym
    - Legendary Hunt: Defeat or catch legendary pokemon (or whatever was randomized into their encounters)
    """
    display_name = "Goal"
    default = 0
    option_champion = 0
    option_steven = 1
    option_norman = 2
    option_legendary_hunt = 3


class RandomizeBadges(Choice):
    """
    Adds Badges to the pool.

    - Vanilla: Gym leaders give their own badge
    - Shuffle: Gym leaders give a random badge
    - Completely Random: Badges can be found anywhere
    """
    display_name = "Randomize Badges"
    default = 2
    option_vanilla = 0
    option_shuffle = 1
    option_completely_random = 2


class RandomizeHms(Choice):
    """
    Adds HMs to the pool.

    - Vanilla: HMs are at their vanilla locations
    - Shuffle: HMs are shuffled among vanilla HM locations
    - Completely Random: HMs can be found anywhere
    """
    display_name = "Randomize HMs"
    default = 2
    option_vanilla = 0
    option_shuffle = 1
    option_completely_random = 2


class RandomizeKeyItems(DefaultOnToggle):
    """
    Adds most key items to the pool.

    These are usually required to unlock a location or region (e.g. Devon Scope, Letter, Basement Key).
    """
    display_name = "Randomize Key Items"


class RandomizeBikes(Toggle):
    """
    Adds the Mach Bike and Acro Bike to the pool.
    """
    display_name = "Randomize Bikes"


class RandomizeEventTickets(Toggle):
    """
    Adds the event tickets to the pool, which let you access legendaries by sailing from Lilycove.
    """
    display_name = "Randomize Event Tickets"


class RandomizeRods(Toggle):
    """
    Adds fishing rods to the pool.
    """
    display_name = "Randomize Fishing Rods"


class RandomizeOverworldItems(DefaultOnToggle):
    """
    Adds items on the ground with a Pokeball sprite to the pool.
    """
    display_name = "Randomize Overworld Items"


class RandomizeHiddenItems(Toggle):
    """
    Adds hidden items to the pool.
    """
    display_name = "Randomize Hidden Items"


class RandomizeNpcGifts(Toggle):
    """
    Adds most gifts received from NPCs to the pool (not including key items or HMs).
    """
    display_name = "Randomize NPC Gifts"


class RandomizeBerryTrees(Toggle):
    """
    Adds berry trees to the pool. Empty soil patches are converted to locations and contribute Sitrus Berries to the pool.
    """
    display_name = "Randomize Berry Trees"


class Dexsanity(Toggle):
    """
    Adding a "caught" pokedex entry gives you an item (catching, evolving, trading, etc.). Only wild encounters are considered logical access to a species.

    Blacklisting wild encounters removes the dexsanity location.

    Defeating gym leaders provides dex info, allowing you to see where on the map you can catch species you need.

    Each pokedex entry adds a Poke Ball, Great Ball, or Ultra Ball to the pool.

    Warning: This adds a lot of locations and will slow you down significantly.
    """
    display_name = "Dexsanity"


class Trainersanity(Toggle):
    """
    Defeating a trainer gives you an item.

    Trainers are no longer missable. Trainers no longer give you money for winning. Each trainer adds a valuable item (Nugget, Stardust, etc.) to the pool.

    Warning: This adds a lot of locations and will slow you down significantly.
    """
    display_name = "Trainersanity"


class ItemPoolType(Choice):
    """
    Determines which non-progression items get put into the item pool.

    - Shuffled: Item pool consists of shuffled vanilla items
    - Diverse Balanced: Item pool consists of random items approximately proportioned according to what they're replacing
    - Diverse: Item pool consists of uniformly random (non-unique) items
    """
    display_name = "Item Pool Type"
    default = 0
    option_shuffled = 0
    option_diverse_balanced = 1
    option_diverse = 2


class HiddenItemsRequireItemfinder(DefaultOnToggle):
    """
    The Itemfinder is logically required to pick up hidden items.
    """
    display_name = "Require Itemfinder"


class DarkCavesRequireFlash(Choice):
    """
    Determines whether HM05 Flash is logically required to navigate a dark cave.
    """
    display_name = "Require Flash"
    default = 3
    option_neither = 0
    option_only_granite_cave = 1
    option_only_victory_road = 2
    option_both = 3


class EliteFourRequirement(Choice):
    """
    Sets the requirements to challenge the elite four.

    - Badges: Obtain some number of badges
    - Gyms: Defeat some number of gyms
    """
    display_name = "Elite Four Requirement"
    default = 0
    option_badges = 0
    option_gyms = 1


class EliteFourCount(Range):
    """
    Sets the number of badges/gyms required to challenge the elite four.
    """
    display_name = "Elite Four Count"
    range_start = 0
    range_end = 8
    default = 8


class NormanRequirement(Choice):
    """
    Sets the requirements to challenge the Petalburg Gym.

    - Badges: Obtain some number of badges
    - Gyms: Defeat some number of gym leaders
    """
    display_name = "Norman Requirement"
    default = 0
    option_badges = 0
    option_gyms = 1


class NormanCount(Range):
    """
    Sets the number of badges/gyms required to challenge the Petalburg Gym.
    """
    display_name = "Norman Count"
    range_start = 0
    range_end = 7
    default = 4


class LegendaryHuntCatch(Toggle):
    """
    Sets whether legendaries need to be caught to satisfy the Legendary Hunt win condition.

    Defeated legendaries can be respawned by defeating the Elite 4.
    """
    display_name = "Legendary Hunt Requires Catching"


class LegendaryHuntCount(Range):
    """
    Sets the number of legendaries that must be caught/defeated for the Legendary Hunt goal.
    """
    display_name = "Legendary Hunt Count"
    range_start = 1
    range_end = 12
    default = 3


class AllowedLegendaryHuntEncounters(OptionSet):
    """
    Sets which legendary encounters can contribute to the Legendary Hunt goal.

    Latias will always be at Southern Island. Latios will always be the roamer. The TV broadcast describing the roamer gives you "seen" info for Latios.

    The braille puzzle in Sealed Chamber gives you "seen" info for Wailord and Relicanth. The move tutor in Fortree City always teaches Dig.
    """
    display_name = "Allowed Legendary Hunt Encounters"
    valid_keys = [
        "Groudon",
        "Kyogre",
        "Rayquaza",
        "Latios",
        "Latias",
        "Regirock",
        "Registeel",
        "Regice",
        "Ho-Oh",
        "Lugia",
        "Deoxys",
        "Mew",
    ]
    default = valid_keys.copy()


class RandomizeWildPokemon(Choice):
    """
    Randomizes wild pokemon encounters (grass, caves, water, fishing).

    Warning: Matching both base stats and type may severely limit the variety for certain pokemon.

    - Vanilla: Wild encounters are unchanged
    - Match Base Stats: Wild pokemon are replaced with species with approximately the same bst
    - Match Type: Wild pokemon are replaced with species that share a type with the original
    - Match Base Stats and Type: Apply both Match Base Stats and Match Type
    - Completely Random: There are no restrictions
    """
    display_name = "Randomize Wild Pokemon"
    default = 0
    option_vanilla = 0
    option_match_base_stats = 1
    option_match_type = 2
    option_match_base_stats_and_type = 3
    option_completely_random = 4


class WildEncounterBlacklist(OptionSet):
    """
    Prevents listed species from appearing in the wild when wild encounters are randomized.

    May be overridden if enforcing other restrictions in combination with this blacklist is impossible.

    Use "_Legendaries" as a shortcut for all legendary pokemon.
    """
    display_name = "Wild Encounter Blacklist"
    valid_keys = ["_Legendaries"] + sorted([species.label for species in data.species.values()])


class RandomizeStarters(Choice):
    """
    Randomizes the starter pokemon in Professor Birch's bag.

    - Vanilla: Starters are unchanged
    - Match Base Stats: Starters are replaced with species with approximately the same bst
    - Match Type: Starters are replaced with species that share a type with the original
    - Match Base Stats and Type: Apply both Match Base Stats and Match Type
    - Completely Random: There are no restrictions
    """
    display_name = "Randomize Starters"
    default = 0
    option_vanilla = 0
    option_match_base_stats = 1
    option_match_type = 2
    option_match_base_stats_and_type = 3
    option_completely_random = 4


class StarterBlacklist(OptionSet):
    """
    Prevents listed species from appearing as starters when starters are randomized.

    May be overridden if enforcing other restrictions in combination with this blacklist is impossible.

    Use "_Legendaries" as a shortcut for all legendary pokemon.
    """
    display_name = "Starter Blacklist"
    valid_keys = ["_Legendaries"] + sorted([species.label for species in data.species.values()])


class RandomizeTrainerParties(Choice):
    """
    Randomizes the parties of all trainers.

    Warning: Matching both base stats and type may severely limit the variety for certain pokemon.

    - Vanilla: Parties are unchanged
    - Match Base Stats: Trainer pokemon are replaced with species with approximately the same bst
    - Match Type: Trainer pokemon are replaced with species that share a type with the original
    - Match Base Stats and Type: Apply both Match Base Stats and Match Type
    - Completely Random: There are no restrictions
    """
    display_name = "Randomize Trainer Parties"
    default = 0
    option_vanilla = 0
    option_match_base_stats = 1
    option_match_type = 2
    option_match_base_stats_and_type = 3
    option_completely_random = 4


class TrainerPartyBlacklist(OptionSet):
    """
    Prevents listed species from appearing in opponent trainers' parties if opponent parties are randomized.

    May be overridden if enforcing other restrictions in combination with this blacklist is impossible.

    Use "_Legendaries" as a shortcut for all legendary pokemon.
    """
    display_name = "Trainer Party Blacklist"
    valid_keys = ["_Legendaries"] + sorted([species.label for species in data.species.values()])


class ForceFullyEvolved(Range):
    """
    When an opponent uses a pokemon of the specified level or higher, restricts the species to only fully evolved pokemon.

    Only applies when trainer parties are randomized.

    Warning: Combining a low value with matched base stats may severely limit the variety for certain pokemon.
    """
    display_name = "Force Fully Evolved"
    range_start = 1
    range_end = 100
    default = 100


class RandomizeLegendaryEncounters(Choice):
    """
    Randomizes legendary encounters (Rayquaza, Regice, Latias, etc.). The roamer will always be Latios during legendary hunts.

    - Vanilla: Legendary encounters are unchanged
    - Shuffle: Legendary encounters are shuffled between each other
    - Match Base Stats: Legendary encounters are replaced with species with approximately the same bst
    - Match Type: Legendary encounters are replaced with species that share a type with the original
    - Match Base Stats and Type: Apply both Match Base Stats and Match Type
    - Completely Random: There are no restrictions
    """
    display_name = "Randomize Legendary Encounters"
    default = 0
    option_vanilla = 0
    option_shuffle = 1
    option_match_base_stats = 2
    option_match_type = 3
    option_match_base_stats_and_type = 4
    option_completely_random = 5


class RandomizeMiscPokemon(Choice):
    """
    Randomizes non-legendary static encounters. May grow to include other pokemon like trades or gifts.

    - Vanilla: Species are unchanged
    - Shuffle: Species are shuffled between each other
    - Match Base Stats: Species are replaced with species with approximately the same bst
    - Match Type: Species are replaced with species that share a type with the original
    - Match Base Stats and Type: Apply both Match Base Stats and Match Type
    - Completely Random: There are no restrictions
    """
    display_name = "Randomize Misc Pokemon"
    default = 0
    option_vanilla = 0
    option_shuffle = 1
    option_match_base_stats = 2
    option_match_type = 3
    option_match_base_stats_and_type = 4
    option_completely_random = 5


class RandomizeTypes(Choice):
    """
    Randomizes the type(s) of every pokemon. Each species will have the same number of types.

    - Vanilla: Types are unchanged
    - Shuffle: Types are shuffled globally for all species (e.g. every Water-type pokemon becomes Fire-type)
    - Completely Random: Each species has its type(s) randomized
    - Follow Evolutions: Types are randomized per evolution line instead of per species
    """
    display_name = "Randomize Types"
    default = 0
    option_vanilla = 0
    option_shuffle = 1
    option_completely_random = 2
    option_follow_evolutions = 3


class RandomizeAbilities(Choice):
    """
    Randomizes abilities of every species. Each species will have the same number of abilities.

    - Vanilla: Abilities are unchanged
    - Completely Random: Each species has its abilities randomized
    - Follow Evolutions: Abilities are randomized, but if a pokemon would normally retain its ability when evolving, the random ability will also be retained
    """
    display_name = "Randomize Abilities"
    default = 0
    option_vanilla = 0
    option_completely_random = 1
    option_follow_evolutions = 2


class AbilityBlacklist(OptionSet):
    """
    Prevent species from being given these abilities.

    Has no effect if abilities are not randomized.
    """
    display_name = "Ability Blacklist"
    valid_keys = sorted([ability.label for ability in data.abilities])


class LevelUpMoves(Choice):
    """
    Randomizes the moves a pokemon learns when they reach a level where they would learn a move. Your starter is guaranteed to have a usable damaging move.

    - Vanilla: Learnset is unchanged
    - Randomized: Moves are randomized
    - Start with Four Moves: Moves are randomized and all Pokemon know 4 moves at level 1
    """
    display_name = "Level Up Moves"
    default = 0
    option_vanilla = 0
    option_randomized = 1
    option_start_with_four_moves = 2


class MoveMatchTypeBias(Range):
    """
    Sets the probability that a learned move will be forced match one of the types of a pokemon.

    If a move is not forced to match type, it will roll for Normal type bias.
    """
    display_name = "Move Match Type Bias"
    range_start = 0
    range_end = 100
    default = 0


class MoveNormalTypeBias(Range):
    """
    After it has been decided that a move will not be forced to match types, sets the probability that a learned move will be forced to be the Normal type.

    If a move is not forced to be Normal, it will be completely random.
    """
    display_name = "Move Normal Type Bias"
    range_start = 0
    range_end = 100
    default = 0


class MoveBlacklist(OptionSet):
    """
    Prevents species from learning these moves via learnsets, TMs, and move tutors.

    HM moves are already banned.
    """
    display_name = "Move Blacklist"
    valid_keys = sorted(data.move_labels.keys())


class HmCompatibility(NamedRange):
    """
    Sets the percent chance that a given HM is compatible with a species.

    Some opponents like gym leaders are allowed to use HMs. This option can affect the moves they know.
    """
    display_name = "HM Compatibility"
    default = -1
    range_start = 50
    range_end = 100
    special_range_names = {
        "vanilla": -1,
        "full": 100,
    }


class TmTutorCompatibility(NamedRange):
    """
    Sets the percent chance that a given TM or move tutor is compatible with a species.

    Some opponents like gym leaders are allowed to use TMs. This option can affect the moves they know.
    """
    display_name = "TM/Tutor Compatibility"
    default = -1
    range_start = 0
    range_end = 100
    special_range_names = {
        "vanilla": -1,
        "full": 100,
    }


class TmTutorMoves(Toggle):
    """
    Randomizes the moves taught by TMs and move tutors.

    Some opponents like gym leaders are allowed to use TMs. This option can affect the moves they know.
    """
    display_name = "TM/Tutor Moves"


class ReusableTmsTutors(Toggle):
    """
    Sets TMs to not break after use (they remain sellable). Sets move tutors to infinite use.
    """
    display_name = "Reusable TMs and Tutors"


class MinCatchRate(Range):
    """
    Sets the minimum catch rate a pokemon can have. Any pokemon with a catch rate below this floor will have it raised to this value.

    Legendaries are often in the single digits
    Fully evolved pokemon are often double digits
    Pidgey is 255
    """
    display_name = "Minimum Catch Rate"
    range_start = 3
    range_end = 255
    default = 3


class GuaranteedCatch(Toggle):
    """
    Every throw is guaranteed to catch a wild pokemon.
    """
    display_name = "Guaranteed Catch"


class NormalizeEncounterRates(Toggle):
    """
    Make every slot on an encounter table approximately equally likely.

    This does NOT mean each species is equally likely. In the vanilla game, each species may occupy more than one slot, and slots vary in probability.
    
    Species will still occupy the same slots as vanilla, but the slots will be equally weighted. The minimum encounter rate will be 8% (higher in water).
    """
    display_name = "Normalize Encounter Rates"


class ExpModifier(Range):
    """
    Multiplies gained experience by a percentage.

    100 is default
    50 is half
    200 is double
    etc.
    """
    display_name = "Exp Modifier"
    range_start = 0
    range_end = 1000
    default = 100


class BlindTrainers(Toggle):
    """
    Trainers will not start a battle with you unless you talk to them.
    """
    display_name = "Blind Trainers"


class PurgeSpinners(Toggle):
    """
    Trainers will rotate in predictable patterns on a set interval instead of randomly and don't turn toward you when you run.
    """
    display_name = "Purge Spinners"


class MatchTrainerLevels(Choice):
    """
    When you start a battle with a trainer, your party's levels will be automatically set to match that trainer's highest level pokemon.

    The experience you receive will match your party's average actual level, and will only be awarded when you win the battle.

    This is a pseudo-replacement for a level cap and makes every trainer battle a fair fight while still allowing you to level up.

    - Off: The vanilla experience
    - Additive: The modifier you apply to your team is a flat bonus
    - Multiplicative: The modifier you apply to your team is a percent bonus
    """
    display_name = "Match Trainer Levels"
    default = 0
    option_off = 0
    option_additive = 1
    option_multiplicative = 2


class MatchTrainerLevelsBonus(Range):
    """
    A level bonus (or penalty) to apply to your team when matching an opponent's levels.

    When the match trainer levels option is "additive", this value is added to your team's levels during a battle.
        For example, if this value is 5 (+5 levels), you'll have a level 25 team against a level 20 team, and a level 45 team against a level 40 team.

    When the match trainer levels option is "multiplicative", this is a percent bonus.
        For example, if this value is 5 (+5%), you'll have a level 21 team against a level 20 team, and a level 42 team against a level 40 team.
    """
    display_name = "Match Trainer Levels Modifier"
    range_start = -100
    range_end = 100
    default = 0


class DoubleBattleChance(Range):
    """
    The percent chance that a trainer with more than 1 pokemon will be converted into a double battle.

    If these trainers would normally approach you, they will only do so if you have 2 unfainted pokemon.

    They can be battled by talking to them no matter what.
    """
    display_name = "Double Battle Chance"
    range_start = 0
    range_end = 100
    default = 0


class BetterShops(Toggle):
    """
    Pokemarts sell every item that can be obtained in a pokemart (except mail, which is still unique to the relevant city).
    """
    display_name = "Better Shops"


class RemoveRoadblocks(OptionSet):
    """
    Removes specific NPCs that normally stand in your way until certain events are completed.

    This can open up the world a bit and make your playthrough less linear, but be careful how many you remove; it may make too much of your world accessible upon receiving Surf.
    """
    display_name = "Remove Roadblocks"
    valid_keys = [
        "Route 110 Aqua Grunts",
        "Route 112 Magma Grunts",
        "Route 119 Aqua Grunts",
        "Safari Zone Construction Workers",
        "Lilycove City Wailmer",
        "Aqua Hideout Grunts",
        "Seafloor Cavern Aqua Grunt",
    ]


class ExtraBoulders(Toggle):
    """
    Places strength boulders on Route 115 which block access to Meteor Falls from the beach.

    This aims to take some power away from Surf by restricting how much it allows you to access.
    """
    display_name = "Extra Boulders"


class ExtraBumpySlope(Toggle):
    """
    Adds a bumpy slope to Route 115 which allows access to Meteor Falls if you have the Acro Bike.

    This aims to take some power away from Surf by adding a new way to exit the Rustboro area.
    """
    display_name = "Extra Bumpy Slope"


class ModifyRoute118(Toggle):
    """
    Changes the layout of Route 118 so that it must be crossed with the Acro Bike instead of Surf.

    This aims to take some power away from Surf by restricting how much it allows you to access.
    """
    display_name = "Modify Route 118"


class FreeFlyLocation(Toggle):
    """
    Enables flying to one random location (excluding cities reachable with no items).
    """
    display_name = "Free Fly Location"


class FreeFlyBlacklist(OptionSet):
    """
    Disables specific locations as valid free fly locations.

    Has no effect if Free Fly Location is disabled.
    """
    display_name = "Free Fly Blacklist"
    valid_keys = [
        "Littleroot Town",
        "Oldale Town",
        "Petalburg City",
        "Rustboro City",
        "Dewford Town",
        "Slateport City",
        "Mauville City",
        "Verdanturf Town",
        "Fallarbor Town",
        "Lavaridge Town",
        "Fortree City",
        "Lilycove City",
        "Mossdeep City",
        "Sootopolis City",
        "Ever Grande City",
    ]
    default = [
        "Littleroot Town",
        "Oldale Town",
        "Petalburg City",
        "Rustboro City",
        "Dewford Town",
    ]


class HmRequirements(Choice):
    """
    Sets the requirements to use HMs outside of battle.
    """
    display_name = "HM Requirements"
    default = 0
    option_vanilla = 0
    option_fly_without_badge = 1


class TurboA(Toggle):
    """
    Holding A will advance most text automatically.
    """
    display_name = "Turbo A"


class ReceiveItemMessages(Choice):
    """
    Determines whether you receive an in-game notification when receiving an item. Items can still only be received in the overworld.

    - All: Every item shows a message
    - Progression: Only progression items show a message
    - None: All items are added to your bag silently (badges will still show).
    """
    display_name = "Receive Item Messages"
    default = 0
    option_all = 0
    option_progression = 1
    option_none = 2


class RemoteItems(Toggle):
    """
    Instead of placing your own items directly into the ROM, all items are received from the server, including items you find for yourself.

    This enables co-op of a single slot and recovering more items after a lost save file (if you're so unlucky).

    But it changes pickup behavior slightly and requires connection to the server to receive any items.
    """
    display_name = "Remote Items"


class RandomizeMusic(Toggle):
    """
    Shuffles music played in any situation where it loops. Includes many FRLG tracks.
    """
    display_name = "Randomize Music"


class RandomizeFanfares(Toggle):
    """
    Shuffles fanfares for item pickups, healing at the pokecenter, etc.

    When this option is enabled, pressing B will interrupt most fanfares.
    """
    display_name = "Randomize Fanfares"


class PokemonEmeraldDeathLink(DeathLink):
    __doc__ = DeathLink.__doc__ + "\n\n    In Pokemon Emerald, whiting out sends a death and receiving a death causes you to white out."


class WonderTrading(DefaultOnToggle):
    """
    Allows participation in wonder trading with other players in your current multiworld. Speak with the center receptionist on the second floor of any pokecenter.

    Wonder trading NEVER affects logic.

    Certain aspects of a pokemon species are per-game, not per-pokemon. As a result, some things are not retained during a trade, including type, ability, level up learnset, and so on.

    Receiving a pokemon this way does not mark it as found in your pokedex.

    Trade evolutions do not evolve this way; they retain their modified methods (level ups and item use).
    """
    display_name = "Wonder Trading"


class EasterEgg(FreeText):
    """
    Enter certain phrases and something special might happen.

    All secret phrases are something that could be a trendy phrase in Dewford Town. They are case insensitive.
    """
    display_name = "Easter Egg"
    default = "EMERALD SECRET"


class PokemonEmeraldStartInventory(StartInventory):
    """
    Start with these items.

    They will be in your PC, which you can access from your home or a pokemon center.
    """


@dataclass
class PokemonEmeraldOptions(PerGameCommonOptions):
    goal: Goal

    badges: RandomizeBadges
    hms: RandomizeHms
    key_items: RandomizeKeyItems
    bikes: RandomizeBikes
    event_tickets: RandomizeEventTickets
    rods: RandomizeRods
    overworld_items: RandomizeOverworldItems
    hidden_items: RandomizeHiddenItems
    npc_gifts: RandomizeNpcGifts
    berry_trees: RandomizeBerryTrees
    dexsanity: Dexsanity
    trainersanity: Trainersanity
    item_pool_type: ItemPoolType

    require_itemfinder: HiddenItemsRequireItemfinder
    require_flash: DarkCavesRequireFlash
    elite_four_requirement: EliteFourRequirement
    elite_four_count: EliteFourCount
    norman_requirement: NormanRequirement
    norman_count: NormanCount
    legendary_hunt_catch: LegendaryHuntCatch
    legendary_hunt_count: LegendaryHuntCount
    allowed_legendary_hunt_encounters: AllowedLegendaryHuntEncounters

    wild_pokemon: RandomizeWildPokemon
    wild_encounter_blacklist: WildEncounterBlacklist
    starters: RandomizeStarters
    starter_blacklist: StarterBlacklist
    trainer_parties: RandomizeTrainerParties
    trainer_party_blacklist: TrainerPartyBlacklist
    force_fully_evolved: ForceFullyEvolved
    legendary_encounters: RandomizeLegendaryEncounters
    misc_pokemon: RandomizeMiscPokemon
    types: RandomizeTypes
    abilities: RandomizeAbilities
    ability_blacklist: AbilityBlacklist

    level_up_moves: LevelUpMoves
    move_match_type_bias: MoveMatchTypeBias
    move_normal_type_bias: MoveNormalTypeBias
    tm_tutor_compatibility: TmTutorCompatibility
    hm_compatibility: HmCompatibility
    tm_tutor_moves: TmTutorMoves
    reusable_tms_tutors: ReusableTmsTutors
    move_blacklist: MoveBlacklist

    min_catch_rate: MinCatchRate
    guaranteed_catch: GuaranteedCatch
    normalize_encounter_rates: NormalizeEncounterRates
    exp_modifier: ExpModifier
    blind_trainers: BlindTrainers
    purge_spinners: PurgeSpinners
    match_trainer_levels: MatchTrainerLevels
    match_trainer_levels_bonus: MatchTrainerLevelsBonus
    double_battle_chance: DoubleBattleChance
    better_shops: BetterShops

    remove_roadblocks: RemoveRoadblocks
    extra_boulders: ExtraBoulders
    extra_bumpy_slope: ExtraBumpySlope
    modify_118: ModifyRoute118
    free_fly_location: FreeFlyLocation
    free_fly_blacklist: FreeFlyBlacklist
    hm_requirements: HmRequirements

    turbo_a: TurboA
    receive_item_messages: ReceiveItemMessages
    remote_items: RemoteItems

    music: RandomizeMusic
    fanfares: RandomizeFanfares

    death_link: PokemonEmeraldDeathLink

    enable_wonder_trading: WonderTrading
    easter_egg: EasterEgg

    start_inventory: PokemonEmeraldStartInventory


OPTION_GROUPS = [
    OptionGroup(
        "Item & Location Options", [
            PokemonEmeraldStartInventory,
        ], True,
    ),
]
