diff --git a/worlds/kh2/__init__.py b/worlds/kh2/__init__.py index 06036c68..cc50c86e 100644 --- a/worlds/kh2/__init__.py +++ b/worlds/kh2/__init__.py @@ -33,6 +33,7 @@ class KH2World(World): game: str = "Kingdom Hearts 2" web = KingdomHearts2Web() data_version = 1 + required_client_version = (0, 4, 0) option_definitions = KH2_Options item_name_to_id = {name: data.code for name, data in item_dictionary_table.items()} location_name_to_id = {item_name: data.code for item_name, data in all_locations.items() if data.code} @@ -40,6 +41,7 @@ class KH2World(World): def __init__(self, multiworld: "MultiWorld", player: int): super().__init__(multiworld, player) + self.valid_abilities = None self.visitlocking_dict = None self.plando_locations = None self.luckyemblemamount = None @@ -81,8 +83,6 @@ class KH2World(World): return created_item def create_items(self) -> None: - itempool: typing.List[Item] = [] - self.visitlocking_dict = Progression_Dicts["AllVisitLocking"].copy() if self.multiworld.Schmovement[self.player] != "level_0": for _ in range(self.multiworld.Schmovement[self.player].value): @@ -128,9 +128,7 @@ class KH2World(World): self.visitlocking_dict.pop(item) self.multiworld.push_precollected(self.create_item(item)) - for item in item_dictionary_table: - data = self.item_quantity_dict[item] - itempool += [self.create_item(item) for _ in range(data)] + itempool = [self.create_item(item) for item, data in self.item_quantity_dict.items() for _ in range(data)] # Creating filler for unfilled locations itempool += [self.create_filler() @@ -139,14 +137,12 @@ class KH2World(World): def generate_early(self) -> None: # Item Quantity dict because Abilities can be a problem for KH2's Software. - for item, data in item_dictionary_table.items(): - self.item_quantity_dict[item] = data.quantity + self.item_quantity_dict = {item: data.quantity for item, data in item_dictionary_table.items()} # Dictionary to mark locations with their plandoed item # Example. Final Xemnas: Victory self.plando_locations = dict() self.hitlist = [] self.starting_invo_verify() - # Option to turn off Promise Charm Item if not self.multiworld.Promise_Charm[self.player]: self.item_quantity_dict[ItemName.PromiseCharm] = 0 @@ -243,43 +239,64 @@ class KH2World(World): def keyblade_fill(self): if self.multiworld.KeybladeAbilities[self.player] == "support": - self.sora_keyblade_ability_pool.extend(SupportAbility_Table.keys()) + self.sora_keyblade_ability_pool = { + **{item: data for item, data in self.item_quantity_dict.items() if item in SupportAbility_Table}, + **{ItemName.NegativeCombo: 1, ItemName.AirComboPlus: 1, ItemName.ComboPlus: 1, + ItemName.FinishingPlus: 1}} + elif self.multiworld.KeybladeAbilities[self.player] == "action": - self.sora_keyblade_ability_pool.extend(ActionAbility_Table.keys()) + self.sora_keyblade_ability_pool = {item: data for item, data in self.item_quantity_dict.items() if item in ActionAbility_Table} + # there are too little action abilities so 2 random support abilities are placed + for _ in range(3): + randomSupportAbility = self.multiworld.per_slot_randoms[self.player].choice(list(SupportAbility_Table.keys())) + while randomSupportAbility in self.sora_keyblade_ability_pool: + randomSupportAbility = self.multiworld.per_slot_randoms[self.player].choice( + list(SupportAbility_Table.keys())) + self.sora_keyblade_ability_pool[randomSupportAbility] = 1 else: # both action and support on keyblades. # TODO: make option to just exclude scom - self.sora_keyblade_ability_pool.extend(ActionAbility_Table.keys()) - self.sora_keyblade_ability_pool.extend(SupportAbility_Table.keys()) + self.sora_keyblade_ability_pool = { + **{item: data for item, data in self.item_quantity_dict.items() if item in SupportAbility_Table}, + **{item: data for item, data in self.item_quantity_dict.items() if item in ActionAbility_Table}, + **{ItemName.NegativeCombo: 1, ItemName.AirComboPlus: 1, ItemName.ComboPlus: 1, ItemName.FinishingPlus: 1}} for ability in self.multiworld.BlacklistKeyblade[self.player].value: if ability in self.sora_keyblade_ability_pool: - self.sora_keyblade_ability_pool.remove(ability) + self.sora_keyblade_ability_pool.pop(ability) - while len(self.sora_keyblade_ability_pool) < len(self.keyblade_slot_copy): - self.sora_keyblade_ability_pool.append( - self.multiworld.per_slot_randoms[self.player].choice(list(SupportAbility_Table.keys()))) + # magic number for amount of keyblades + if sum(self.sora_keyblade_ability_pool.values()) < 28: + raise Exception(f"{self.multiworld.get_file_safe_player_name(self.player)} has too little Keyblade Abilities in the Keyblade Pool") + self.valid_abilities = list(self.sora_keyblade_ability_pool.keys()) # Kingdom Key cannot have No Experience so plandoed here instead of checking 26 times if its kingdom key - random_ability = self.multiworld.per_slot_randoms[self.player].choice(self.sora_keyblade_ability_pool) + random_ability = self.multiworld.per_slot_randoms[self.player].choice(self.valid_abilities) while random_ability == ItemName.NoExperience: - random_ability = self.multiworld.per_slot_randoms[self.player].choice(self.sora_keyblade_ability_pool) + random_ability = self.multiworld.per_slot_randoms[self.player].choice(self.validAbilitys) self.plando_locations[LocationName.KingdomKeySlot] = random_ability self.item_quantity_dict[random_ability] -= 1 - self.sora_keyblade_ability_pool.remove(random_ability) + self.sora_keyblade_ability_pool[random_ability] -= 1 + if self.sora_keyblade_ability_pool[random_ability] == 0: + self.valid_abilities.remove(random_ability) + self.sora_keyblade_ability_pool.pop(random_ability) # plando keyblades because they can only have abilities for keyblade in self.keyblade_slot_copy: - random_ability = self.multiworld.per_slot_randoms[self.player].choice(self.sora_keyblade_ability_pool) + random_ability = self.multiworld.per_slot_randoms[self.player].choice(self.valid_abilities) self.plando_locations[keyblade] = random_ability self.item_quantity_dict[random_ability] -= 1 - self.sora_keyblade_ability_pool.remove(random_ability) + self.sora_keyblade_ability_pool[random_ability] -= 1 + if self.sora_keyblade_ability_pool[random_ability] == 0: + self.valid_abilities.remove(random_ability) + self.sora_keyblade_ability_pool.pop(random_ability) self.totalLocations -= 1 def starting_invo_verify(self): for item, value in self.multiworld.start_inventory[self.player].value.items(): if item in ActionAbility_Table \ - or item in SupportAbility_Table or exclusionItem_table["StatUps"]: + or item in SupportAbility_Table or exclusionItem_table["StatUps"] \ + or item in DonaldAbility_Table or item in GoofyAbility_Table: # cannot have more than the quantity for abilties if value > item_dictionary_table[item].quantity: logging.info(