| 
									
										
										
										
											2021-10-21 20:23:13 +02:00
										 |  |  | from argparse import Namespace | 
					
						
							| 
									
										
										
										
											2024-03-11 17:22:30 -05:00
										 |  |  | from typing import List, Optional, Tuple, Type, Union | 
					
						
							| 
									
										
										
										
											2021-10-21 20:23:13 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-02 03:01:59 -05:00
										 |  |  | from BaseClasses import CollectionState, Item, ItemClassification, Location, MultiWorld, Region | 
					
						
							| 
									
										
										
										
											2024-03-11 17:22:30 -05:00
										 |  |  | from worlds.AutoWorld import World, call_all | 
					
						
							| 
									
										
										
										
											2021-10-21 20:23:13 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-26 19:13:24 -06:00
										 |  |  | gen_steps = ("generate_early", "create_regions", "create_items", "set_rules", "generate_basic", "pre_fill") | 
					
						
							| 
									
										
										
										
											2021-10-21 20:23:13 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-10 12:47:45 -05:00
										 |  |  | def setup_solo_multiworld( | 
					
						
							|  |  |  |     world_type: Type[World], steps: Tuple[str, ...] = gen_steps, seed: Optional[int] = None | 
					
						
							|  |  |  | ) -> MultiWorld: | 
					
						
							| 
									
										
										
										
											2023-10-22 06:00:27 -05:00
										 |  |  |     """
 | 
					
						
							|  |  |  |     Creates a multiworld with a single player of `world_type`, sets default options, and calls provided gen steps. | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     :param world_type: Type of the world to generate a multiworld for | 
					
						
							|  |  |  |     :param steps: The gen steps that should be called on the generated multiworld before returning. Default calls | 
					
						
							|  |  |  |     steps through pre_fill | 
					
						
							| 
									
										
										
										
											2024-03-10 12:47:45 -05:00
										 |  |  |     :param seed: The seed to be used when creating this multiworld | 
					
						
							| 
									
										
										
										
											2024-05-02 03:01:59 -05:00
										 |  |  |     :return: The generated multiworld | 
					
						
							| 
									
										
										
										
											2023-10-22 06:00:27 -05:00
										 |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2024-03-11 17:22:30 -05:00
										 |  |  |     return setup_multiworld(world_type, steps, seed) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def setup_multiworld(worlds: Union[List[Type[World]], Type[World]], steps: Tuple[str, ...] = gen_steps, | 
					
						
							| 
									
										
										
										
											2024-05-02 03:01:59 -05:00
										 |  |  |                      seed: Optional[int] = None) -> MultiWorld: | 
					
						
							| 
									
										
										
										
											2024-03-11 17:22:30 -05:00
										 |  |  |     """
 | 
					
						
							|  |  |  |     Creates a multiworld with a player for each provided world type, allowing duplicates, setting default options, and | 
					
						
							|  |  |  |     calling the provided gen steps. | 
					
						
							|  |  |  |      | 
					
						
							| 
									
										
										
										
											2024-05-02 03:01:59 -05:00
										 |  |  |     :param worlds: Type/s of worlds to generate a multiworld for | 
					
						
							|  |  |  |     :param steps: Gen steps that should be called before returning. Default calls through pre_fill | 
					
						
							| 
									
										
										
										
											2024-03-11 17:22:30 -05:00
										 |  |  |     :param seed: The seed to be used when creating this multiworld | 
					
						
							| 
									
										
										
										
											2024-05-02 03:01:59 -05:00
										 |  |  |     :return: The generated multiworld | 
					
						
							| 
									
										
										
										
											2024-03-11 17:22:30 -05:00
										 |  |  |     """
 | 
					
						
							|  |  |  |     if not isinstance(worlds, list): | 
					
						
							|  |  |  |         worlds = [worlds] | 
					
						
							|  |  |  |     players = len(worlds) | 
					
						
							|  |  |  |     multiworld = MultiWorld(players) | 
					
						
							|  |  |  |     multiworld.game = {player: world_type.game for player, world_type in enumerate(worlds, 1)} | 
					
						
							|  |  |  |     multiworld.player_name = {player: f"Tester{player}" for player in multiworld.player_ids} | 
					
						
							| 
									
										
										
										
											2024-03-10 12:47:45 -05:00
										 |  |  |     multiworld.set_seed(seed) | 
					
						
							| 
									
										
										
										
											2023-10-10 15:30:20 -05:00
										 |  |  |     multiworld.state = CollectionState(multiworld) | 
					
						
							| 
									
										
										
										
											2021-10-21 20:23:13 +02:00
										 |  |  |     args = Namespace() | 
					
						
							| 
									
										
										
										
											2024-03-11 17:22:30 -05:00
										 |  |  |     for player, world_type in enumerate(worlds, 1): | 
					
						
							|  |  |  |         for key, option in world_type.options_dataclass.type_hints.items(): | 
					
						
							|  |  |  |             updated_options = getattr(args, key, {}) | 
					
						
							|  |  |  |             updated_options[player] = option.from_any(option.default) | 
					
						
							|  |  |  |             setattr(args, key, updated_options) | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     multiworld.set_options(args) | 
					
						
							| 
									
										
										
										
											2023-02-26 19:13:24 -06:00
										 |  |  |     for step in steps: | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |         call_all(multiworld, step) | 
					
						
							|  |  |  |     return multiworld | 
					
						
							| 
									
										
										
										
											2024-05-02 03:01:59 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class TestWorld(World): | 
					
						
							|  |  |  |     game = f"Test Game" | 
					
						
							|  |  |  |     item_name_to_id = {} | 
					
						
							|  |  |  |     location_name_to_id = {} | 
					
						
							|  |  |  |     hidden = True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def generate_test_multiworld(players: int = 1) -> MultiWorld: | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Generates a multiworld using a special Test Case World class, and seed of 0. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     :param players: Number of players to generate the multiworld for | 
					
						
							|  |  |  |     :return: The generated test multiworld | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     multiworld = setup_multiworld([TestWorld] * players, seed=0) | 
					
						
							|  |  |  |     multiworld.regions += [Region("Menu", player_id + 1, multiworld) for player_id in range(players)] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return multiworld | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def generate_locations(count: int, player_id: int, region: Region, address: Optional[int] = None, | 
					
						
							|  |  |  |                        tag: str = "") -> List[Location]: | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Generates the specified amount of locations for the player and adds them to the specified region. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     :param count: Number of locations to create | 
					
						
							|  |  |  |     :param player_id: ID of the player to create the locations for | 
					
						
							|  |  |  |     :param address: Address for the specified locations. They will all share the same address if multiple are created | 
					
						
							|  |  |  |     :param region: Parent region to add these locations to | 
					
						
							|  |  |  |     :param tag: Tag to add to the name of the generated locations | 
					
						
							|  |  |  |     :return: List containing the created locations | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     prefix = f"player{player_id}{tag}_location" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     locations = [Location(player_id, f"{prefix}{i}", address, region) for i in range(count)] | 
					
						
							|  |  |  |     region.locations += locations | 
					
						
							|  |  |  |     return locations | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def generate_items(count: int, player_id: int, advancement: bool = False, code: int = None) -> List[Item]: | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Generates the specified amount of items for the target player. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     :param count: The amount of items to create | 
					
						
							|  |  |  |     :param player_id: ID of the player to create the items for | 
					
						
							|  |  |  |     :param advancement: Whether the created items should be advancement | 
					
						
							|  |  |  |     :param code: The code the items should be created with | 
					
						
							|  |  |  |     :return: List containing the created items | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     item_type = "prog" if advancement else "" | 
					
						
							|  |  |  |     classification = ItemClassification.progression if advancement else ItemClassification.filler | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     items = [Item(f"player{player_id}_{item_type}item{i}", classification, code, player_id) for i in range(count)] | 
					
						
							|  |  |  |     return items |