| 
									
										
										
										
											2023-03-20 12:19:55 -04:00
										 |  |  | from BaseClasses import Tutorial, ItemClassification | 
					
						
							| 
									
										
										
										
											2023-03-22 10:21:41 -04:00
										 |  |  | import logging | 
					
						
							| 
									
										
										
										
											2023-03-20 12:19:55 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | from .Items import * | 
					
						
							| 
									
										
										
										
											2023-04-23 16:20:43 -04:00
										 |  |  | from .Locations import all_locations, setup_locations, exclusion_table, AllWeaponSlot | 
					
						
							| 
									
										
										
										
											2023-03-20 12:19:55 -04:00
										 |  |  | from .Names import ItemName, LocationName | 
					
						
							|  |  |  | from .OpenKH import patch_kh2 | 
					
						
							|  |  |  | from .Options import KH2_Options | 
					
						
							|  |  |  | from .Regions import create_regions, connect_regions | 
					
						
							|  |  |  | from .Rules import set_rules | 
					
						
							|  |  |  | from ..AutoWorld import World, WebWorld | 
					
						
							|  |  |  | from .logic import KH2Logic | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class KingdomHearts2Web(WebWorld): | 
					
						
							|  |  |  |     tutorials = [Tutorial( | 
					
						
							|  |  |  |             "Multiworld Setup Guide", | 
					
						
							|  |  |  |             "A guide to playing Kingdom Hearts 2 Final Mix with Archipelago.", | 
					
						
							|  |  |  |             "English", | 
					
						
							|  |  |  |             "setup_en.md", | 
					
						
							|  |  |  |             "setup/en", | 
					
						
							|  |  |  |             ["JaredWeakStrike"] | 
					
						
							|  |  |  |     )] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # noinspection PyUnresolvedReferences | 
					
						
							|  |  |  | class KH2World(World): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Kingdom Hearts II is an action role-playing game developed and published by Square Enix and released in 2005. | 
					
						
							|  |  |  |     It is the sequel to Kingdom Hearts and Kingdom Hearts: Chain of Memories, and like the two previous games, | 
					
						
							|  |  |  |     focuses on Sora and his friends' continued battle against the Darkness. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     game: str = "Kingdom Hearts 2" | 
					
						
							|  |  |  |     web = KingdomHearts2Web() | 
					
						
							|  |  |  |     data_version = 1 | 
					
						
							| 
									
										
										
										
											2023-04-15 15:17:23 -04:00
										 |  |  |     required_client_version = (0, 4, 0) | 
					
						
							| 
									
										
										
										
											2023-03-20 12:19:55 -04:00
										 |  |  |     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} | 
					
						
							|  |  |  |     item_name_groups = item_groups | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, multiworld: "MultiWorld", player: int): | 
					
						
							|  |  |  |         super().__init__(multiworld, player) | 
					
						
							| 
									
										
										
										
											2023-04-15 15:17:23 -04:00
										 |  |  |         self.valid_abilities = None | 
					
						
							| 
									
										
										
										
											2023-04-09 22:12:23 -04:00
										 |  |  |         self.visitlocking_dict = None | 
					
						
							|  |  |  |         self.plando_locations = None | 
					
						
							|  |  |  |         self.luckyemblemamount = None | 
					
						
							|  |  |  |         self.luckyemblemrequired = None | 
					
						
							| 
									
										
										
										
											2023-03-20 12:19:55 -04:00
										 |  |  |         self.BountiesRequired = None | 
					
						
							|  |  |  |         self.BountiesAmount = None | 
					
						
							|  |  |  |         self.hitlist = None | 
					
						
							|  |  |  |         self.LocalItems = {} | 
					
						
							|  |  |  |         self.RandomSuperBoss = list() | 
					
						
							|  |  |  |         self.filler_items = list() | 
					
						
							|  |  |  |         self.item_quantity_dict = {} | 
					
						
							|  |  |  |         self.donald_ability_pool = list() | 
					
						
							|  |  |  |         self.goofy_ability_pool = list() | 
					
						
							|  |  |  |         self.sora_keyblade_ability_pool = list() | 
					
						
							|  |  |  |         self.keyblade_slot_copy = list(Locations.Keyblade_Slots.keys()) | 
					
						
							| 
									
										
										
										
											2023-03-27 13:17:06 -04:00
										 |  |  |         self.keyblade_slot_copy.remove(LocationName.KingdomKeySlot) | 
					
						
							| 
									
										
										
										
											2023-03-20 12:19:55 -04:00
										 |  |  |         self.totalLocations = len(all_locations.items()) | 
					
						
							|  |  |  |         self.growth_list = list() | 
					
						
							|  |  |  |         for x in range(4): | 
					
						
							|  |  |  |             self.growth_list.extend(Movement_Table.keys()) | 
					
						
							| 
									
										
										
										
											2023-04-23 16:20:43 -04:00
										 |  |  |         self.slotDataDuping = set() | 
					
						
							|  |  |  |         self.localItems = dict() | 
					
						
							| 
									
										
										
										
											2023-03-20 12:19:55 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def fill_slot_data(self) -> dict: | 
					
						
							| 
									
										
										
										
											2023-04-23 16:20:43 -04:00
										 |  |  |         for values in CheckDupingItems.values(): | 
					
						
							|  |  |  |             if isinstance(values, set): | 
					
						
							|  |  |  |                 self.slotDataDuping = self.slotDataDuping.union(values) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 for inner_values in values.values(): | 
					
						
							|  |  |  |                     self.slotDataDuping = self.slotDataDuping.union(inner_values) | 
					
						
							|  |  |  |         self.LocalItems = {location.address: item_dictionary_table[location.item.name].code | 
					
						
							|  |  |  |                            for location in self.multiworld.get_filled_locations(self.player) | 
					
						
							|  |  |  |                            if location.item.player == self.player | 
					
						
							|  |  |  |                            and location.item.name in self.slotDataDuping | 
					
						
							|  |  |  |                            and location.name not in AllWeaponSlot} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-09 22:12:23 -04:00
										 |  |  |         return {"hitlist":              self.hitlist, | 
					
						
							|  |  |  |                 "LocalItems":           self.LocalItems, | 
					
						
							|  |  |  |                 "Goal":                 self.multiworld.Goal[self.player].value, | 
					
						
							|  |  |  |                 "FinalXemnas":          self.multiworld.FinalXemnas[self.player].value, | 
					
						
							|  |  |  |                 "LuckyEmblemsRequired": self.multiworld.LuckyEmblemsRequired[self.player].value, | 
					
						
							|  |  |  |                 "BountyRequired":       self.multiworld.BountyRequired[self.player].value} | 
					
						
							| 
									
										
										
										
											2023-03-20 12:19:55 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def create_item(self, name: str, ) -> Item: | 
					
						
							|  |  |  |         data = item_dictionary_table[name] | 
					
						
							|  |  |  |         if name in Progression_Dicts["Progression"]: | 
					
						
							|  |  |  |             item_classification = ItemClassification.progression | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             item_classification = ItemClassification.filler | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         created_item = KH2Item(name, item_classification, data.code, self.player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return created_item | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-09 22:12:23 -04:00
										 |  |  |     def create_items(self) -> None: | 
					
						
							|  |  |  |         self.visitlocking_dict = Progression_Dicts["AllVisitLocking"].copy() | 
					
						
							|  |  |  |         if self.multiworld.Schmovement[self.player] != "level_0": | 
					
						
							|  |  |  |             for _ in range(self.multiworld.Schmovement[self.player].value): | 
					
						
							|  |  |  |                 for name in {ItemName.HighJump, ItemName.QuickRun, ItemName.DodgeRoll, ItemName.AerialDodge, | 
					
						
							|  |  |  |                              ItemName.Glide}: | 
					
						
							|  |  |  |                     self.item_quantity_dict[name] -= 1 | 
					
						
							|  |  |  |                     self.growth_list.remove(name) | 
					
						
							|  |  |  |                     self.multiworld.push_precollected(self.create_item(name)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if self.multiworld.RandomGrowth[self.player] != 0: | 
					
						
							|  |  |  |             max_growth = min(self.multiworld.RandomGrowth[self.player].value, len(self.growth_list)) | 
					
						
							|  |  |  |             for _ in range(max_growth): | 
					
						
							|  |  |  |                 random_growth = self.multiworld.per_slot_randoms[self.player].choice(self.growth_list) | 
					
						
							|  |  |  |                 self.item_quantity_dict[random_growth] -= 1 | 
					
						
							|  |  |  |                 self.growth_list.remove(random_growth) | 
					
						
							|  |  |  |                 self.multiworld.push_precollected(self.create_item(random_growth)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if self.multiworld.Visitlocking[self.player] == "no_visit_locking": | 
					
						
							|  |  |  |             for item, amount in Progression_Dicts["AllVisitLocking"].items(): | 
					
						
							|  |  |  |                 for _ in range(amount): | 
					
						
							|  |  |  |                     self.multiworld.push_precollected(self.create_item(item)) | 
					
						
							|  |  |  |                     self.item_quantity_dict[item] -= 1 | 
					
						
							|  |  |  |                     self.visitlocking_dict[item] -= 1 | 
					
						
							|  |  |  |                     if self.visitlocking_dict[item] == 0: | 
					
						
							|  |  |  |                         self.visitlocking_dict.pop(item) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         elif self.multiworld.Visitlocking[self.player] == "second_visit_locking": | 
					
						
							|  |  |  |             for item in Progression_Dicts["2VisitLocking"]: | 
					
						
							|  |  |  |                 self.item_quantity_dict[item] -= 1 | 
					
						
							|  |  |  |                 self.visitlocking_dict[item] -= 1 | 
					
						
							|  |  |  |                 if self.visitlocking_dict[item] == 0: | 
					
						
							|  |  |  |                     self.visitlocking_dict.pop(item) | 
					
						
							|  |  |  |                 self.multiworld.push_precollected(self.create_item(item)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for _ in range(self.multiworld.RandomVisitLockingItem[self.player].value): | 
					
						
							|  |  |  |             if sum(self.visitlocking_dict.values()) <= 0: | 
					
						
							|  |  |  |                 break | 
					
						
							|  |  |  |             visitlocking_set = list(self.visitlocking_dict.keys()) | 
					
						
							|  |  |  |             item = self.multiworld.per_slot_randoms[self.player].choice(visitlocking_set) | 
					
						
							|  |  |  |             self.item_quantity_dict[item] -= 1 | 
					
						
							|  |  |  |             self.visitlocking_dict[item] -= 1 | 
					
						
							|  |  |  |             if self.visitlocking_dict[item] == 0: | 
					
						
							|  |  |  |                 self.visitlocking_dict.pop(item) | 
					
						
							|  |  |  |             self.multiworld.push_precollected(self.create_item(item)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-15 15:17:23 -04:00
										 |  |  |         itempool = [self.create_item(item) for item, data in self.item_quantity_dict.items() for _ in range(data)] | 
					
						
							| 
									
										
										
										
											2023-04-09 22:12:23 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Creating filler for unfilled locations | 
					
						
							|  |  |  |         itempool += [self.create_filler() | 
					
						
							| 
									
										
										
										
											2023-04-23 16:20:43 -04:00
										 |  |  |                      for _ in range(self.totalLocations - len(itempool))] | 
					
						
							| 
									
										
										
										
											2023-04-09 22:12:23 -04:00
										 |  |  |         self.multiworld.itempool += itempool | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 12:19:55 -04:00
										 |  |  |     def generate_early(self) -> None: | 
					
						
							|  |  |  |         # Item Quantity dict because Abilities can be a problem for KH2's Software. | 
					
						
							| 
									
										
										
										
											2023-04-15 15:17:23 -04:00
										 |  |  |         self.item_quantity_dict = {item: data.quantity for item, data in item_dictionary_table.items()} | 
					
						
							| 
									
										
										
										
											2023-04-09 22:12:23 -04:00
										 |  |  |         # Dictionary to mark locations with their plandoed item | 
					
						
							|  |  |  |         # Example. Final Xemnas: Victory | 
					
						
							|  |  |  |         self.plando_locations = dict() | 
					
						
							|  |  |  |         self.hitlist = [] | 
					
						
							|  |  |  |         self.starting_invo_verify() | 
					
						
							| 
									
										
										
										
											2023-03-20 12:19:55 -04:00
										 |  |  |         # Option to turn off Promise Charm Item | 
					
						
							|  |  |  |         if not self.multiworld.Promise_Charm[self.player]: | 
					
						
							|  |  |  |             self.item_quantity_dict[ItemName.PromiseCharm] = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-09 22:12:23 -04:00
										 |  |  |         self.set_excluded_locations() | 
					
						
							| 
									
										
										
										
											2023-03-20 12:19:55 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if self.multiworld.Goal[self.player] == "lucky_emblem_hunt": | 
					
						
							| 
									
										
										
										
											2023-04-09 22:12:23 -04:00
										 |  |  |             self.luckyemblemamount = self.multiworld.LuckyEmblemsAmount[self.player].value | 
					
						
							|  |  |  |             self.luckyemblemrequired = self.multiworld.LuckyEmblemsRequired[self.player].value | 
					
						
							|  |  |  |             self.emblem_verify() | 
					
						
							| 
									
										
										
										
											2023-03-20 12:19:55 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # hitlist | 
					
						
							|  |  |  |         elif self.multiworld.Goal[self.player] == "hitlist": | 
					
						
							|  |  |  |             self.RandomSuperBoss.extend(exclusion_table["Hitlist"]) | 
					
						
							|  |  |  |             self.BountiesAmount = self.multiworld.BountyAmount[self.player].value | 
					
						
							|  |  |  |             self.BountiesRequired = self.multiworld.BountyRequired[self.player].value | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-09 22:12:23 -04:00
										 |  |  |             self.hitlist_verify() | 
					
						
							| 
									
										
										
										
											2023-03-20 12:19:55 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-09 22:12:23 -04:00
										 |  |  |             for bounty in range(self.BountiesAmount): | 
					
						
							|  |  |  |                 randomBoss = self.multiworld.per_slot_randoms[self.player].choice(self.RandomSuperBoss) | 
					
						
							|  |  |  |                 self.plando_locations[randomBoss] = ItemName.Bounty | 
					
						
							|  |  |  |                 self.hitlist.append(self.location_name_to_id[randomBoss]) | 
					
						
							|  |  |  |                 self.RandomSuperBoss.remove(randomBoss) | 
					
						
							|  |  |  |                 self.totalLocations -= 1 | 
					
						
							| 
									
										
										
										
											2023-03-20 12:19:55 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-09 22:12:23 -04:00
										 |  |  |         self.donald_fill() | 
					
						
							|  |  |  |         self.goofy_fill() | 
					
						
							|  |  |  |         self.keyblade_fill() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if self.multiworld.FinalXemnas[self.player]: | 
					
						
							|  |  |  |             self.plando_locations[LocationName.FinalXemnas] = ItemName.Victory | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.plando_locations[LocationName.FinalXemnas] = self.create_filler().name | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # same item placed because you can only get one of these 2 locations | 
					
						
							|  |  |  |         # they are both under the same flag so the player gets both locations just one of the two items | 
					
						
							|  |  |  |         random_stt_item = self.create_filler().name | 
					
						
							|  |  |  |         for location in {LocationName.JunkMedal, LocationName.JunkMedal}: | 
					
						
							|  |  |  |             self.plando_locations[location] = random_stt_item | 
					
						
							|  |  |  |         self.level_subtraction() | 
					
						
							|  |  |  |         # subtraction from final xemnas and stt | 
					
						
							|  |  |  |         self.totalLocations -= 3 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def pre_fill(self): | 
					
						
							|  |  |  |         for location, item in self.plando_locations.items(): | 
					
						
							|  |  |  |             self.multiworld.get_location(location, self.player).place_locked_item( | 
					
						
							|  |  |  |                     self.create_item(item)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def create_regions(self): | 
					
						
							|  |  |  |         location_table = setup_locations() | 
					
						
							|  |  |  |         create_regions(self.multiworld, self.player, location_table) | 
					
						
							|  |  |  |         connect_regions(self.multiworld, self.player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def set_rules(self): | 
					
						
							|  |  |  |         set_rules(self.multiworld, self.player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def generate_output(self, output_directory: str): | 
					
						
							|  |  |  |         patch_kh2(self, output_directory) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def donald_fill(self): | 
					
						
							|  |  |  |         for item in DonaldAbility_Table: | 
					
						
							| 
									
										
										
										
											2023-03-20 12:19:55 -04:00
										 |  |  |             data = self.item_quantity_dict[item] | 
					
						
							|  |  |  |             for _ in range(data): | 
					
						
							|  |  |  |                 self.donald_ability_pool.append(item) | 
					
						
							|  |  |  |             self.item_quantity_dict[item] = 0 | 
					
						
							|  |  |  |             # 32 is the amount of donald abilities | 
					
						
							|  |  |  |         while len(self.donald_ability_pool) < 32: | 
					
						
							| 
									
										
										
										
											2023-04-09 22:12:23 -04:00
										 |  |  |             self.donald_ability_pool.append( | 
					
						
							|  |  |  |                     self.multiworld.per_slot_randoms[self.player].choice(self.donald_ability_pool)) | 
					
						
							|  |  |  |             # Placing Donald Abilities on donald locations | 
					
						
							|  |  |  |         for donaldLocation in Locations.Donald_Checks.keys(): | 
					
						
							|  |  |  |             random_ability = self.multiworld.per_slot_randoms[self.player].choice(self.donald_ability_pool) | 
					
						
							|  |  |  |             self.plando_locations[donaldLocation] = random_ability | 
					
						
							|  |  |  |             self.totalLocations -= 1 | 
					
						
							|  |  |  |             self.donald_ability_pool.remove(random_ability) | 
					
						
							| 
									
										
										
										
											2023-03-20 12:19:55 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-09 22:12:23 -04:00
										 |  |  |     def goofy_fill(self): | 
					
						
							| 
									
										
										
										
											2023-03-20 12:19:55 -04:00
										 |  |  |         for item in GoofyAbility_Table.keys(): | 
					
						
							|  |  |  |             data = self.item_quantity_dict[item] | 
					
						
							|  |  |  |             for _ in range(data): | 
					
						
							|  |  |  |                 self.goofy_ability_pool.append(item) | 
					
						
							|  |  |  |             self.item_quantity_dict[item] = 0 | 
					
						
							|  |  |  |             # 32 is the amount of goofy abilities | 
					
						
							|  |  |  |         while len(self.goofy_ability_pool) < 33: | 
					
						
							| 
									
										
										
										
											2023-04-09 22:12:23 -04:00
										 |  |  |             self.goofy_ability_pool.append( | 
					
						
							|  |  |  |                     self.multiworld.per_slot_randoms[self.player].choice(self.goofy_ability_pool)) | 
					
						
							|  |  |  |         # Placing Goofy Abilities on goofy locations | 
					
						
							|  |  |  |         for goofyLocation in Locations.Goofy_Checks.keys(): | 
					
						
							|  |  |  |             random_ability = self.multiworld.per_slot_randoms[self.player].choice(self.goofy_ability_pool) | 
					
						
							|  |  |  |             self.plando_locations[goofyLocation] = random_ability | 
					
						
							|  |  |  |             self.totalLocations -= 1 | 
					
						
							|  |  |  |             self.goofy_ability_pool.remove(random_ability) | 
					
						
							| 
									
										
										
										
											2023-03-20 12:19:55 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-09 22:12:23 -04:00
										 |  |  |     def keyblade_fill(self): | 
					
						
							|  |  |  |         if self.multiworld.KeybladeAbilities[self.player] == "support": | 
					
						
							| 
									
										
										
										
											2023-04-15 15:17:23 -04:00
										 |  |  |             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}} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-09 22:12:23 -04:00
										 |  |  |         elif self.multiworld.KeybladeAbilities[self.player] == "action": | 
					
						
							| 
									
										
										
										
											2023-04-23 16:20:43 -04:00
										 |  |  |             self.sora_keyblade_ability_pool = {item: data for item, data in self.item_quantity_dict.items() if | 
					
						
							|  |  |  |                                                item in ActionAbility_Table} | 
					
						
							| 
									
										
										
										
											2023-04-15 15:17:23 -04:00
										 |  |  |             # there are too little action abilities so 2 random support abilities are placed | 
					
						
							|  |  |  |             for _ in range(3): | 
					
						
							| 
									
										
										
										
											2023-04-23 16:20:43 -04:00
										 |  |  |                 randomSupportAbility = self.multiworld.per_slot_randoms[self.player].choice( | 
					
						
							|  |  |  |                         list(SupportAbility_Table.keys())) | 
					
						
							| 
									
										
										
										
											2023-04-15 15:17:23 -04:00
										 |  |  |                 while randomSupportAbility in self.sora_keyblade_ability_pool: | 
					
						
							|  |  |  |                     randomSupportAbility = self.multiworld.per_slot_randoms[self.player].choice( | 
					
						
							| 
									
										
										
										
											2023-04-23 16:20:43 -04:00
										 |  |  |                             list(SupportAbility_Table.keys())) | 
					
						
							| 
									
										
										
										
											2023-04-15 15:17:23 -04:00
										 |  |  |                 self.sora_keyblade_ability_pool[randomSupportAbility] = 1 | 
					
						
							| 
									
										
										
										
											2023-03-20 12:19:55 -04:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2023-04-09 22:12:23 -04:00
										 |  |  |             # both action and support on keyblades. | 
					
						
							|  |  |  |             # TODO: make option to just exclude scom | 
					
						
							| 
									
										
										
										
											2023-04-15 15:17:23 -04:00
										 |  |  |             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}, | 
					
						
							| 
									
										
										
										
											2023-04-23 16:20:43 -04:00
										 |  |  |                 **{ItemName.NegativeCombo: 1, ItemName.AirComboPlus: 1, ItemName.ComboPlus: 1, | 
					
						
							|  |  |  |                    ItemName.FinishingPlus: 1}} | 
					
						
							| 
									
										
										
										
											2023-03-20 12:19:55 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-09 22:12:23 -04:00
										 |  |  |         for ability in self.multiworld.BlacklistKeyblade[self.player].value: | 
					
						
							|  |  |  |             if ability in self.sora_keyblade_ability_pool: | 
					
						
							| 
									
										
										
										
											2023-04-15 15:17:23 -04:00
										 |  |  |                 self.sora_keyblade_ability_pool.pop(ability) | 
					
						
							| 
									
										
										
										
											2023-04-09 22:12:23 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-15 15:17:23 -04:00
										 |  |  |         # magic number for amount of keyblades | 
					
						
							|  |  |  |         if sum(self.sora_keyblade_ability_pool.values()) < 28: | 
					
						
							| 
									
										
										
										
											2023-04-23 16:20:43 -04:00
										 |  |  |             raise Exception( | 
					
						
							|  |  |  |                     f"{self.multiworld.get_file_safe_player_name(self.player)} has too little Keyblade Abilities in the Keyblade Pool") | 
					
						
							| 
									
										
										
										
											2023-03-20 12:19:55 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-15 15:17:23 -04:00
										 |  |  |         self.valid_abilities = list(self.sora_keyblade_ability_pool.keys()) | 
					
						
							| 
									
										
										
										
											2023-03-27 13:17:06 -04:00
										 |  |  |         #  Kingdom Key cannot have No Experience so plandoed here instead of checking 26 times if its kingdom key | 
					
						
							| 
									
										
										
										
											2023-04-15 15:17:23 -04:00
										 |  |  |         random_ability = self.multiworld.per_slot_randoms[self.player].choice(self.valid_abilities) | 
					
						
							| 
									
										
										
										
											2023-03-27 13:17:06 -04:00
										 |  |  |         while random_ability == ItemName.NoExperience: | 
					
						
							| 
									
										
										
										
											2023-04-15 16:04:08 -04:00
										 |  |  |             random_ability = self.multiworld.per_slot_randoms[self.player].choice(self.valid_abilities) | 
					
						
							| 
									
										
										
										
											2023-04-09 22:12:23 -04:00
										 |  |  |         self.plando_locations[LocationName.KingdomKeySlot] = random_ability | 
					
						
							| 
									
										
										
										
											2023-03-27 13:17:06 -04:00
										 |  |  |         self.item_quantity_dict[random_ability] -= 1 | 
					
						
							| 
									
										
										
										
											2023-04-15 15:17:23 -04:00
										 |  |  |         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) | 
					
						
							| 
									
										
										
										
											2023-03-27 13:17:06 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 12:19:55 -04:00
										 |  |  |         # plando keyblades because they can only have abilities | 
					
						
							|  |  |  |         for keyblade in self.keyblade_slot_copy: | 
					
						
							| 
									
										
										
										
											2023-04-15 15:17:23 -04:00
										 |  |  |             random_ability = self.multiworld.per_slot_randoms[self.player].choice(self.valid_abilities) | 
					
						
							| 
									
										
										
										
											2023-04-09 22:12:23 -04:00
										 |  |  |             self.plando_locations[keyblade] = random_ability | 
					
						
							| 
									
										
										
										
											2023-03-20 12:19:55 -04:00
										 |  |  |             self.item_quantity_dict[random_ability] -= 1 | 
					
						
							| 
									
										
										
										
											2023-04-15 15:17:23 -04:00
										 |  |  |             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) | 
					
						
							| 
									
										
										
										
											2023-03-20 12:19:55 -04:00
										 |  |  |             self.totalLocations -= 1 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-09 22:12:23 -04:00
										 |  |  |     def starting_invo_verify(self): | 
					
						
							|  |  |  |         for item, value in self.multiworld.start_inventory[self.player].value.items(): | 
					
						
							|  |  |  |             if item in ActionAbility_Table \ | 
					
						
							| 
									
										
										
										
											2023-04-15 15:17:23 -04:00
										 |  |  |                     or item in SupportAbility_Table or exclusionItem_table["StatUps"] \ | 
					
						
							|  |  |  |                     or item in DonaldAbility_Table or item in GoofyAbility_Table: | 
					
						
							| 
									
										
										
										
											2023-04-09 22:12:23 -04:00
										 |  |  |                 # cannot have more than the quantity for abilties | 
					
						
							|  |  |  |                 if value > item_dictionary_table[item].quantity: | 
					
						
							|  |  |  |                     logging.info( | 
					
						
							|  |  |  |                             f"{self.multiworld.get_file_safe_player_name(self.player)} cannot have more than {item_dictionary_table[item].quantity} of {item}" | 
					
						
							|  |  |  |                             f"Changing the amount to the max amount") | 
					
						
							|  |  |  |                     value = item_dictionary_table[item].quantity | 
					
						
							|  |  |  |                 self.item_quantity_dict[item] -= value | 
					
						
							| 
									
										
										
										
											2023-03-20 12:19:55 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-09 22:12:23 -04:00
										 |  |  |     def emblem_verify(self): | 
					
						
							|  |  |  |         if self.luckyemblemamount < self.luckyemblemrequired: | 
					
						
							|  |  |  |             logging.info( | 
					
						
							|  |  |  |                     f"Lucky Emblem Amount {self.multiworld.LuckyEmblemsAmount[self.player].value} is less than required " | 
					
						
							|  |  |  |                     f"{self.multiworld.LuckyEmblemsRequired[self.player].value} for player {self.multiworld.get_file_safe_player_name(self.player)}." | 
					
						
							|  |  |  |                     f" Setting amount to {self.multiworld.LuckyEmblemsRequired[self.player].value}") | 
					
						
							|  |  |  |             luckyemblemamount = max(self.luckyemblemamount, self.luckyemblemrequired) | 
					
						
							|  |  |  |             self.multiworld.LuckyEmblemsAmount[self.player].value = luckyemblemamount | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-15 19:59:01 -04:00
										 |  |  |         self.item_quantity_dict[ItemName.LuckyEmblem] = self.multiworld.LuckyEmblemsAmount[self.player].value | 
					
						
							| 
									
										
										
										
											2023-04-09 22:12:23 -04:00
										 |  |  |         # give this proof to unlock the final door once the player has the amount of lucky emblem required | 
					
						
							|  |  |  |         self.item_quantity_dict[ItemName.ProofofNonexistence] = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def hitlist_verify(self): | 
					
						
							|  |  |  |         for location in self.multiworld.exclude_locations[self.player].value: | 
					
						
							|  |  |  |             if location in self.RandomSuperBoss: | 
					
						
							|  |  |  |                 self.RandomSuperBoss.remove(location) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         #  Testing if the player has the right amount of Bounties for Completion. | 
					
						
							|  |  |  |         if len(self.RandomSuperBoss) < self.BountiesAmount: | 
					
						
							|  |  |  |             logging.info( | 
					
						
							|  |  |  |                     f"{self.multiworld.get_file_safe_player_name(self.player)} has more bounties than bosses." | 
					
						
							|  |  |  |                     f" Setting total bounties to {len(self.RandomSuperBoss)}") | 
					
						
							|  |  |  |             self.BountiesAmount = len(self.RandomSuperBoss) | 
					
						
							|  |  |  |             self.multiworld.BountyAmount[self.player].value = self.BountiesAmount | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if len(self.RandomSuperBoss) < self.BountiesRequired: | 
					
						
							|  |  |  |             logging.info(f"{self.multiworld.get_file_safe_player_name(self.player)} has too many required bounties." | 
					
						
							|  |  |  |                          f" Setting required bounties to {len(self.RandomSuperBoss)}") | 
					
						
							|  |  |  |             self.BountiesRequired = len(self.RandomSuperBoss) | 
					
						
							|  |  |  |             self.multiworld.BountyRequired[self.player].value = self.BountiesRequired | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if self.BountiesAmount < self.BountiesRequired: | 
					
						
							|  |  |  |             logging.info(f"Bounties Amount {self.multiworld.BountyAmount[self.player].value} is less than required " | 
					
						
							|  |  |  |                          f"{self.multiworld.BountyRequired[self.player].value} for player {self.multiworld.get_file_safe_player_name(self.player)}." | 
					
						
							|  |  |  |                          f" Setting amount to {self.multiworld.BountyRequired[self.player].value}") | 
					
						
							|  |  |  |             self.BountiesAmount = max(self.BountiesAmount, self.BountiesRequired) | 
					
						
							|  |  |  |             self.multiworld.BountyAmount[self.player].value = self.BountiesAmount | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.multiworld.start_hints[self.player].value.add(ItemName.Bounty) | 
					
						
							|  |  |  |         self.item_quantity_dict[ItemName.ProofofNonexistence] = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def set_excluded_locations(self): | 
					
						
							|  |  |  |         # Option to turn off all superbosses. Can do this individually but its like 20+ checks | 
					
						
							|  |  |  |         if not self.multiworld.SuperBosses[self.player] and not self.multiworld.Goal[self.player] == "hitlist": | 
					
						
							|  |  |  |             for superboss in exclusion_table["Datas"]: | 
					
						
							|  |  |  |                 self.multiworld.exclude_locations[self.player].value.add(superboss) | 
					
						
							|  |  |  |             for superboss in exclusion_table["SuperBosses"]: | 
					
						
							|  |  |  |                 self.multiworld.exclude_locations[self.player].value.add(superboss) | 
					
						
							| 
									
										
										
										
											2023-03-20 12:19:55 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-09 22:12:23 -04:00
										 |  |  |         # Option to turn off Olympus Colosseum Cups. | 
					
						
							|  |  |  |         if self.multiworld.Cups[self.player] == "no_cups": | 
					
						
							|  |  |  |             for cup in exclusion_table["Cups"]: | 
					
						
							|  |  |  |                 self.multiworld.exclude_locations[self.player].value.add(cup) | 
					
						
							|  |  |  |         # exclude only hades paradox. If cups and hades paradox then nothing is excluded | 
					
						
							|  |  |  |         elif self.multiworld.Cups[self.player] == "cups": | 
					
						
							|  |  |  |             self.multiworld.exclude_locations[self.player].value.add(LocationName.HadesCupTrophyParadoxCups) | 
					
						
							| 
									
										
										
										
											2023-03-20 12:19:55 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-09 22:12:23 -04:00
										 |  |  |     def level_subtraction(self): | 
					
						
							|  |  |  |         # there are levels but level 1 is there for the yamls | 
					
						
							| 
									
										
										
										
											2023-03-20 12:19:55 -04:00
										 |  |  |         if self.multiworld.LevelDepth[self.player] == "level_99_sanity": | 
					
						
							|  |  |  |             # level 99 sanity | 
					
						
							|  |  |  |             self.totalLocations -= 1 | 
					
						
							|  |  |  |         elif self.multiworld.LevelDepth[self.player] == "level_50_sanity": | 
					
						
							|  |  |  |             # level 50 sanity | 
					
						
							|  |  |  |             self.totalLocations -= 50 | 
					
						
							|  |  |  |         elif self.multiworld.LevelDepth[self.player] == "level_1": | 
					
						
							|  |  |  |             # level 1. No checks on levels | 
					
						
							|  |  |  |             self.totalLocations -= 99 | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             # level 50/99 since they contain the same amount of levels | 
					
						
							|  |  |  |             self.totalLocations -= 76 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-09 22:12:23 -04:00
										 |  |  |     def get_filler_item_name(self) -> str: | 
					
						
							| 
									
										
										
										
											2023-04-23 16:20:43 -04:00
										 |  |  |         return self.multiworld.random.choice( | 
					
						
							|  |  |  |                 [ItemName.PowerBoost, ItemName.MagicBoost, ItemName.DefenseBoost, ItemName.APBoost]) |