| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | import typing | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-26 06:24:54 -04:00
										 |  |  | from typing import List, Set, Tuple, Dict | 
					
						
							| 
									
										
										
										
											2022-06-17 03:23:27 +02:00
										 |  |  | from BaseClasses import Item, MultiWorld, Location, Tutorial, ItemClassification | 
					
						
							| 
									
										
										
										
											2022-10-01 15:24:05 +02:00
										 |  |  | from worlds.AutoWorld import WebWorld, World | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | from .Items import StarcraftWoLItem, item_table, filler_items, item_name_groups, get_full_item_list, \ | 
					
						
							| 
									
										
										
										
											2022-10-26 06:24:54 -04:00
										 |  |  |     get_basic_units | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | from .Locations import get_locations | 
					
						
							|  |  |  | from .Regions import create_regions | 
					
						
							| 
									
										
										
										
											2022-10-26 06:24:54 -04:00
										 |  |  | from .Options import sc2wol_options, get_option_value, get_option_set_value | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | from .LogicMixin import SC2WoLLogic | 
					
						
							| 
									
										
										
										
											2022-10-26 06:24:54 -04:00
										 |  |  | from .PoolFilter import filter_missions, filter_items, get_item_upgrades | 
					
						
							|  |  |  | from .MissionTables import get_starting_mission_locations, MissionInfo | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Starcraft2WoLWebWorld(WebWorld): | 
					
						
							|  |  |  |     setup = Tutorial( | 
					
						
							|  |  |  |         "Multiworld Setup Guide", | 
					
						
							|  |  |  |         "A guide to setting up the Starcraft 2 randomizer connected to an Archipelago Multiworld", | 
					
						
							|  |  |  |         "English", | 
					
						
							|  |  |  |         "setup_en.md", | 
					
						
							|  |  |  |         "setup/en", | 
					
						
							|  |  |  |         ["TheCondor"] | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     tutorials = [setup] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-26 13:28:10 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | class SC2WoLWorld(World): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     StarCraft II: Wings of Liberty is a science fiction real-time strategy video game developed and published by Blizzard Entertainment. | 
					
						
							|  |  |  |     Command Raynor's Raiders in collecting pieces of the Keystone in order to stop the zerg threat posed by the Queen of Blades. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     game = "Starcraft 2 Wings of Liberty" | 
					
						
							|  |  |  |     web = Starcraft2WoLWebWorld() | 
					
						
							| 
									
										
										
										
											2022-08-19 13:50:44 -07:00
										 |  |  |     data_version = 3 | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     item_name_to_id = {name: data.code for name, data in item_table.items()} | 
					
						
							|  |  |  |     location_name_to_id = {location.name: location.code for location in get_locations(None, None)} | 
					
						
							| 
									
										
										
										
											2022-08-15 16:46:59 -05:00
										 |  |  |     option_definitions = sc2wol_options | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     item_name_groups = item_name_groups | 
					
						
							|  |  |  |     locked_locations: typing.List[str] | 
					
						
							|  |  |  |     location_cache: typing.List[Location] | 
					
						
							| 
									
										
										
										
											2022-05-26 13:28:10 -04:00
										 |  |  |     mission_req_table = {} | 
					
						
							| 
									
										
										
										
											2022-10-26 06:24:54 -04:00
										 |  |  |     final_mission_id: int | 
					
						
							|  |  |  |     victory_item: str | 
					
						
							| 
									
										
										
										
											2022-08-31 20:55:15 +02:00
										 |  |  |     required_client_version = 0, 3, 5 | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, world: MultiWorld, player: int): | 
					
						
							|  |  |  |         super(SC2WoLWorld, self).__init__(world, player) | 
					
						
							|  |  |  |         self.location_cache = [] | 
					
						
							|  |  |  |         self.locked_locations = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def create_item(self, name: str) -> Item: | 
					
						
							|  |  |  |         data = get_full_item_list()[name] | 
					
						
							| 
									
										
										
										
											2022-06-17 03:23:27 +02:00
										 |  |  |         return StarcraftWoLItem(name, data.classification, data.code, self.player) | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def create_regions(self): | 
					
						
							| 
									
										
										
										
											2022-10-26 06:24:54 -04:00
										 |  |  |         self.mission_req_table, self.final_mission_id, self.victory_item = create_regions( | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |             self.multiworld, self.player, get_locations(self.multiworld, self.player), self.location_cache | 
					
						
							| 
									
										
										
										
											2022-10-26 06:24:54 -04:00
										 |  |  |         ) | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def generate_basic(self): | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |         excluded_items = get_excluded_items(self, self.multiworld, self.player) | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |         starter_items = assign_starter_items(self.multiworld, self.player, excluded_items, self.locked_locations) | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |         pool = get_item_pool(self.multiworld, self.player, self.mission_req_table, starter_items, excluded_items, self.location_cache) | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |         fill_item_pool_with_dummy_items(self, self.multiworld, self.player, self.locked_locations, self.location_cache, pool) | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |         self.multiworld.itempool += pool | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def set_rules(self): | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |         setup_events(self.multiworld, self.player, self.locked_locations, self.location_cache) | 
					
						
							|  |  |  |         self.multiworld.completion_condition[self.player] = lambda state: state.has(self.victory_item, self.player) | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def get_filler_item_name(self) -> str: | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |         return self.multiworld.random.choice(filler_items) | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def fill_slot_data(self): | 
					
						
							| 
									
										
										
										
											2022-05-19 03:03:33 +02:00
										 |  |  |         slot_data = {} | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  |         for option_name in sc2wol_options: | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |             option = getattr(self.multiworld, option_name)[self.player] | 
					
						
							| 
									
										
										
										
											2022-05-19 03:03:33 +02:00
										 |  |  |             if type(option.value) in {str, int}: | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  |                 slot_data[option_name] = int(option.value) | 
					
						
							| 
									
										
										
										
											2022-05-26 13:28:10 -04:00
										 |  |  |         slot_req_table = {} | 
					
						
							|  |  |  |         for mission in self.mission_req_table: | 
					
						
							|  |  |  |             slot_req_table[mission] = self.mission_req_table[mission]._asdict() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         slot_data["mission_req"] = slot_req_table | 
					
						
							| 
									
										
										
										
											2022-10-26 06:24:54 -04:00
										 |  |  |         slot_data["final_mission"] = self.final_mission_id | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  |         return slot_data | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def setup_events(world: MultiWorld, player: int, locked_locations: typing.List[str], location_cache: typing.List[Location]): | 
					
						
							|  |  |  |     for location in location_cache: | 
					
						
							| 
									
										
										
										
											2022-06-17 03:23:27 +02:00
										 |  |  |         if location.address is None: | 
					
						
							|  |  |  |             item = Item(location.name, ItemClassification.progression, None, player) | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |             locked_locations.append(location.name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             location.place_locked_item(item) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def get_excluded_items(self: SC2WoLWorld, world: MultiWorld, player: int) -> Set[str]: | 
					
						
							|  |  |  |     excluded_items: Set[str] = set() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if get_option_value(world, player, "upgrade_bonus") == 1: | 
					
						
							|  |  |  |         excluded_items.add("Ultra-Capacitors") | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         excluded_items.add("Vanadium Plating") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if get_option_value(world, player, "bunker_upgrade") == 1: | 
					
						
							|  |  |  |         excluded_items.add("Shrike Turret") | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         excluded_items.add("Fortified Bunker") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for item in world.precollected_items[player]: | 
					
						
							|  |  |  |         excluded_items.add(item.name) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-26 06:24:54 -04:00
										 |  |  |     excluded_items_option = getattr(world, 'excluded_items', []) | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-26 06:24:54 -04:00
										 |  |  |     excluded_items.update(excluded_items_option[player].value) | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-26 06:24:54 -04:00
										 |  |  |     return excluded_items | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-26 13:28:10 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-26 06:24:54 -04:00
										 |  |  | def assign_starter_items(world: MultiWorld, player: int, excluded_items: Set[str], locked_locations: List[str]) -> List[Item]: | 
					
						
							|  |  |  |     non_local_items = world.non_local_items[player].value | 
					
						
							|  |  |  |     if get_option_value(world, player, "early_unit"): | 
					
						
							|  |  |  |         local_basic_unit = tuple(item for item in get_basic_units(world, player) if item not in non_local_items) | 
					
						
							|  |  |  |         if not local_basic_unit: | 
					
						
							|  |  |  |             raise Exception("At least one basic unit must be local") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # The first world should also be the starting world | 
					
						
							|  |  |  |         first_mission = list(world.worlds[player].mission_req_table)[0] | 
					
						
							|  |  |  |         starting_mission_locations = get_starting_mission_locations(world, player) | 
					
						
							|  |  |  |         if first_mission in starting_mission_locations: | 
					
						
							|  |  |  |             first_location = starting_mission_locations[first_mission] | 
					
						
							|  |  |  |         elif first_mission == "In Utter Darkness": | 
					
						
							|  |  |  |             first_location = first_mission + ": Defeat" | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             first_location = first_mission + ": Victory" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return [assign_starter_item(world, player, excluded_items, locked_locations, first_location, local_basic_unit)] | 
					
						
							| 
									
										
										
										
											2022-05-26 13:28:10 -04:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											2022-10-26 06:24:54 -04:00
										 |  |  |         return [] | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def assign_starter_item(world: MultiWorld, player: int, excluded_items: Set[str], locked_locations: List[str], | 
					
						
							| 
									
										
										
										
											2022-10-26 06:24:54 -04:00
										 |  |  |                         location: str, item_list: Tuple[str, ...]) -> Item: | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     item_name = world.random.choice(item_list) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     excluded_items.add(item_name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     item = create_item_with_correct_settings(world, player, item_name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     world.get_location(location, player).place_locked_item(item) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     locked_locations.append(location) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-26 06:24:54 -04:00
										 |  |  |     return item | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-26 06:24:54 -04:00
										 |  |  | def get_item_pool(world: MultiWorld, player: int, mission_req_table: Dict[str, MissionInfo], | 
					
						
							|  |  |  |                   starter_items: List[str], excluded_items: Set[str], location_cache: List[Location]) -> List[Item]: | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  |     pool: List[Item] = [] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-26 06:24:54 -04:00
										 |  |  |     # For the future: goal items like Artifact Shards go here | 
					
						
							|  |  |  |     locked_items = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # YAML items | 
					
						
							|  |  |  |     yaml_locked_items = get_option_set_value(world, player, 'locked_items') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  |     for name, data in item_table.items(): | 
					
						
							|  |  |  |         if name not in excluded_items: | 
					
						
							|  |  |  |             for _ in range(data.quantity): | 
					
						
							|  |  |  |                 item = create_item_with_correct_settings(world, player, name) | 
					
						
							| 
									
										
										
										
											2022-10-26 06:24:54 -04:00
										 |  |  |                 if name in yaml_locked_items: | 
					
						
							|  |  |  |                     locked_items.append(item) | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     pool.append(item) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     existing_items = starter_items + [item for item in world.precollected_items[player]] | 
					
						
							|  |  |  |     existing_names = [item.name for item in existing_items] | 
					
						
							|  |  |  |     # Removing upgrades for excluded items | 
					
						
							|  |  |  |     for item_name in excluded_items: | 
					
						
							|  |  |  |         if item_name in existing_names: | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  |         invalid_upgrades = get_item_upgrades(pool, item_name) | 
					
						
							|  |  |  |         for invalid_upgrade in invalid_upgrades: | 
					
						
							|  |  |  |             pool.remove(invalid_upgrade) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     filtered_pool = filter_items(world, player, mission_req_table, location_cache, pool, existing_items, locked_items) | 
					
						
							|  |  |  |     return filtered_pool | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-19 03:03:33 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | def fill_item_pool_with_dummy_items(self: SC2WoLWorld, world: MultiWorld, player: int, locked_locations: List[str], | 
					
						
							|  |  |  |                                     location_cache: List[Location], pool: List[Item]): | 
					
						
							|  |  |  |     for _ in range(len(location_cache) - len(locked_locations) - len(pool)): | 
					
						
							|  |  |  |         item = create_item_with_correct_settings(world, player, self.get_filler_item_name()) | 
					
						
							|  |  |  |         pool.append(item) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def create_item_with_correct_settings(world: MultiWorld, player: int, name: str) -> Item: | 
					
						
							|  |  |  |     data = item_table[name] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-17 03:23:27 +02:00
										 |  |  |     item = Item(name, data.classification, data.code, player) | 
					
						
							| 
									
										
										
										
											2022-05-18 17:27:38 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-26 13:28:10 -04:00
										 |  |  |     return item |