SC2: Various bugfixes (#1267)
* SC2: Fixed nondeterminism resulting from early unit placement * SC2: Renamed "world" arguments to "multiworld" * SC2: Fixed All-In Ground including anti-air in defense score, fixed error in beats_protoss_deathball * SC2: Fixed No Logic using logic on Beat events * SC2: Fixed /unfinished command failing when All-In available
This commit is contained in:
		
							parent
							
								
									e3f169b4c3
								
							
						
					
					
						commit
						e71ea94fe5
					
				|  | @ -639,6 +639,13 @@ def request_unfinished_missions(ctx: SC2Context): | ||||||
| 
 | 
 | ||||||
|         _, unfinished_missions = calc_unfinished_missions(ctx, unlocks=unlocks) |         _, unfinished_missions = calc_unfinished_missions(ctx, unlocks=unlocks) | ||||||
| 
 | 
 | ||||||
|  |         # Removing All-In from location pool | ||||||
|  |         final_mission = lookup_id_to_mission[ctx.final_mission] | ||||||
|  |         if final_mission in unfinished_missions.keys(): | ||||||
|  |             message = f"Final Mission Available: {final_mission}[{ctx.final_mission}]\n" + message | ||||||
|  |             if unfinished_missions[final_mission] == -1: | ||||||
|  |                 unfinished_missions.pop(final_mission) | ||||||
|  | 
 | ||||||
|         message += ", ".join(f"{mark_up_mission_name(ctx, mission, unlocks)}[{ctx.mission_req_table[mission].id}] " + |         message += ", ".join(f"{mark_up_mission_name(ctx, mission, unlocks)}[{ctx.mission_req_table[mission].id}] " + | ||||||
|                              mark_up_objectives( |                              mark_up_objectives( | ||||||
|                                  f"[{len(unfinished_missions[mission])}/" |                                  f"[{len(unfinished_missions[mission])}/" | ||||||
|  |  | ||||||
|  | @ -157,17 +157,17 @@ basic_units = { | ||||||
|     'Vulture' |     'Vulture' | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| advanced_basic_units = { | advanced_basic_units = basic_units.union({ | ||||||
|     'Reaper', |     'Reaper', | ||||||
|     'Goliath', |     'Goliath', | ||||||
|     'Diamondback', |     'Diamondback', | ||||||
|     'Viking' |     'Viking' | ||||||
| } | }) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def get_basic_units(world: MultiWorld, player: int) -> typing.Set[str]: | def get_basic_units(multiworld: MultiWorld, player: int) -> typing.Set[str]: | ||||||
|     if get_option_value(world, player, 'required_tactics') > 0: |     if get_option_value(multiworld, player, 'required_tactics') > 0: | ||||||
|         return basic_units.union(advanced_basic_units) |         return advanced_basic_units | ||||||
|     else: |     else: | ||||||
|         return basic_units |         return basic_units | ||||||
| 
 | 
 | ||||||
|  | @ -193,7 +193,7 @@ defense_ratings = { | ||||||
| } | } | ||||||
| zerg_defense_ratings = { | zerg_defense_ratings = { | ||||||
|     "Perdition Turret": 2, |     "Perdition Turret": 2, | ||||||
|     # Bunker w/ Firebat |     # Bunker w/ Firebat: 2 | ||||||
|     "Hive Mind Emulator": 3, |     "Hive Mind Emulator": 3, | ||||||
|     "Psi Disruptor": 3 |     "Psi Disruptor": 3 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -18,9 +18,9 @@ class LocationData(NamedTuple): | ||||||
|     rule: Callable = lambda state: True |     rule: Callable = lambda state: True | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def get_locations(world: Optional[MultiWorld], player: Optional[int]) -> Tuple[LocationData, ...]: | def get_locations(multiworld: Optional[MultiWorld], player: Optional[int]) -> Tuple[LocationData, ...]: | ||||||
|     # Note: rules which are ended with or True are rules identified as needed later when restricted units is an option |     # Note: rules which are ended with or True are rules identified as needed later when restricted units is an option | ||||||
|     logic_level = get_option_value(world, player, 'required_tactics') |     logic_level = get_option_value(multiworld, player, 'required_tactics') | ||||||
|     location_table: List[LocationData] = [ |     location_table: List[LocationData] = [ | ||||||
|         LocationData("Liberation Day", "Liberation Day: Victory", SC2WOL_LOC_ID_OFFSET + 100), |         LocationData("Liberation Day", "Liberation Day: Victory", SC2WOL_LOC_ID_OFFSET + 100), | ||||||
|         LocationData("Liberation Day", "Liberation Day: First Statue", SC2WOL_LOC_ID_OFFSET + 101), |         LocationData("Liberation Day", "Liberation Day: First Statue", SC2WOL_LOC_ID_OFFSET + 101), | ||||||
|  | @ -30,144 +30,144 @@ def get_locations(world: Optional[MultiWorld], player: Optional[int]) -> Tuple[L | ||||||
|         LocationData("Liberation Day", "Liberation Day: Fifth Statue", SC2WOL_LOC_ID_OFFSET + 105), |         LocationData("Liberation Day", "Liberation Day: Fifth Statue", SC2WOL_LOC_ID_OFFSET + 105), | ||||||
|         LocationData("Liberation Day", "Liberation Day: Sixth Statue", SC2WOL_LOC_ID_OFFSET + 106), |         LocationData("Liberation Day", "Liberation Day: Sixth Statue", SC2WOL_LOC_ID_OFFSET + 106), | ||||||
|         LocationData("The Outlaws", "The Outlaws: Victory", SC2WOL_LOC_ID_OFFSET + 200, |         LocationData("The Outlaws", "The Outlaws: Victory", SC2WOL_LOC_ID_OFFSET + 200, | ||||||
|                      lambda state: state._sc2wol_has_common_unit(world, player)), |                      lambda state: state._sc2wol_has_common_unit(multiworld, player)), | ||||||
|         LocationData("The Outlaws", "The Outlaws: Rebel Base", SC2WOL_LOC_ID_OFFSET + 201, |         LocationData("The Outlaws", "The Outlaws: Rebel Base", SC2WOL_LOC_ID_OFFSET + 201, | ||||||
|                      lambda state: state._sc2wol_has_common_unit(world, player)), |                      lambda state: state._sc2wol_has_common_unit(multiworld, player)), | ||||||
|         LocationData("Zero Hour", "Zero Hour: Victory", SC2WOL_LOC_ID_OFFSET + 300, |         LocationData("Zero Hour", "Zero Hour: Victory", SC2WOL_LOC_ID_OFFSET + 300, | ||||||
|                      lambda state: state._sc2wol_has_common_unit(world, player) and |                      lambda state: state._sc2wol_has_common_unit(multiworld, player) and | ||||||
|                                    state._sc2wol_defense_rating(world, player, True) >= 2 and |                                    state._sc2wol_defense_rating(multiworld, player, True) >= 2 and | ||||||
|                                    (logic_level > 0 or state._sc2wol_has_anti_air(world, player))), |                                    (logic_level > 0 or state._sc2wol_has_anti_air(multiworld, player))), | ||||||
|         LocationData("Zero Hour", "Zero Hour: First Group Rescued", SC2WOL_LOC_ID_OFFSET + 301), |         LocationData("Zero Hour", "Zero Hour: First Group Rescued", SC2WOL_LOC_ID_OFFSET + 301), | ||||||
|         LocationData("Zero Hour", "Zero Hour: Second Group Rescued", SC2WOL_LOC_ID_OFFSET + 302, |         LocationData("Zero Hour", "Zero Hour: Second Group Rescued", SC2WOL_LOC_ID_OFFSET + 302, | ||||||
|                      lambda state: state._sc2wol_has_common_unit(world, player)), |                      lambda state: state._sc2wol_has_common_unit(multiworld, player)), | ||||||
|         LocationData("Zero Hour", "Zero Hour: Third Group Rescued", SC2WOL_LOC_ID_OFFSET + 303, |         LocationData("Zero Hour", "Zero Hour: Third Group Rescued", SC2WOL_LOC_ID_OFFSET + 303, | ||||||
|                      lambda state: state._sc2wol_has_common_unit(world, player) and |                      lambda state: state._sc2wol_has_common_unit(multiworld, player) and | ||||||
|                                    state._sc2wol_defense_rating(world, player, True) >= 2), |                                    state._sc2wol_defense_rating(multiworld, player, True) >= 2), | ||||||
|         LocationData("Evacuation", "Evacuation: Victory", SC2WOL_LOC_ID_OFFSET + 400, |         LocationData("Evacuation", "Evacuation: Victory", SC2WOL_LOC_ID_OFFSET + 400, | ||||||
|                      lambda state: state._sc2wol_has_common_unit(world, player) and |                      lambda state: state._sc2wol_has_common_unit(multiworld, player) and | ||||||
|                                    (logic_level > 0 and state._sc2wol_has_anti_air(world, player) |                                    (logic_level > 0 and state._sc2wol_has_anti_air(multiworld, player) | ||||||
|                                     or state._sc2wol_has_competent_anti_air(world, player))), |                                     or state._sc2wol_has_competent_anti_air(multiworld, player))), | ||||||
|         LocationData("Evacuation", "Evacuation: First Chysalis", SC2WOL_LOC_ID_OFFSET + 401), |         LocationData("Evacuation", "Evacuation: First Chysalis", SC2WOL_LOC_ID_OFFSET + 401), | ||||||
|         LocationData("Evacuation", "Evacuation: Second Chysalis", SC2WOL_LOC_ID_OFFSET + 402, |         LocationData("Evacuation", "Evacuation: Second Chysalis", SC2WOL_LOC_ID_OFFSET + 402, | ||||||
|                      lambda state: state._sc2wol_has_common_unit(world, player)), |                      lambda state: state._sc2wol_has_common_unit(multiworld, player)), | ||||||
|         LocationData("Evacuation", "Evacuation: Third Chysalis", SC2WOL_LOC_ID_OFFSET + 403, |         LocationData("Evacuation", "Evacuation: Third Chysalis", SC2WOL_LOC_ID_OFFSET + 403, | ||||||
|                      lambda state: state._sc2wol_has_common_unit(world, player)), |                      lambda state: state._sc2wol_has_common_unit(multiworld, player)), | ||||||
|         LocationData("Outbreak", "Outbreak: Victory", SC2WOL_LOC_ID_OFFSET + 500, |         LocationData("Outbreak", "Outbreak: Victory", SC2WOL_LOC_ID_OFFSET + 500, | ||||||
|                      lambda state: state._sc2wol_defense_rating(world, player, True, False) >= 4 and |                      lambda state: state._sc2wol_defense_rating(multiworld, player, True, False) >= 4 and | ||||||
|                                    (state._sc2wol_has_common_unit(world, player) or state.has("Reaper", player))), |                                    (state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player))), | ||||||
|         LocationData("Outbreak", "Outbreak: Left Infestor", SC2WOL_LOC_ID_OFFSET + 501, |         LocationData("Outbreak", "Outbreak: Left Infestor", SC2WOL_LOC_ID_OFFSET + 501, | ||||||
|                      lambda state: state._sc2wol_defense_rating(world, player, True, False) >= 2 and |                      lambda state: state._sc2wol_defense_rating(multiworld, player, True, False) >= 2 and | ||||||
|                                    (state._sc2wol_has_common_unit(world, player) or state.has("Reaper", player))), |                                    (state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player))), | ||||||
|         LocationData("Outbreak", "Outbreak: Right Infestor", SC2WOL_LOC_ID_OFFSET + 502, |         LocationData("Outbreak", "Outbreak: Right Infestor", SC2WOL_LOC_ID_OFFSET + 502, | ||||||
|                      lambda state: state._sc2wol_defense_rating(world, player, True, False) >= 2 and |                      lambda state: state._sc2wol_defense_rating(multiworld, player, True, False) >= 2 and | ||||||
|                                    (state._sc2wol_has_common_unit(world, player) or state.has("Reaper", player))), |                                    (state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player))), | ||||||
|         LocationData("Safe Haven", "Safe Haven: Victory", SC2WOL_LOC_ID_OFFSET + 600, |         LocationData("Safe Haven", "Safe Haven: Victory", SC2WOL_LOC_ID_OFFSET + 600, | ||||||
|                      lambda state: state._sc2wol_has_common_unit(world, player) and |                      lambda state: state._sc2wol_has_common_unit(multiworld, player) and | ||||||
|                                    state._sc2wol_has_competent_anti_air(world, player)), |                                    state._sc2wol_has_competent_anti_air(multiworld, player)), | ||||||
|         LocationData("Safe Haven", "Safe Haven: North Nexus", SC2WOL_LOC_ID_OFFSET + 601, |         LocationData("Safe Haven", "Safe Haven: North Nexus", SC2WOL_LOC_ID_OFFSET + 601, | ||||||
|                      lambda state: state._sc2wol_has_common_unit(world, player) and |                      lambda state: state._sc2wol_has_common_unit(multiworld, player) and | ||||||
|                                    state._sc2wol_has_competent_anti_air(world, player)), |                                    state._sc2wol_has_competent_anti_air(multiworld, player)), | ||||||
|         LocationData("Safe Haven", "Safe Haven: East Nexus", SC2WOL_LOC_ID_OFFSET + 602, |         LocationData("Safe Haven", "Safe Haven: East Nexus", SC2WOL_LOC_ID_OFFSET + 602, | ||||||
|                      lambda state: state._sc2wol_has_common_unit(world, player) and |                      lambda state: state._sc2wol_has_common_unit(multiworld, player) and | ||||||
|                                    state._sc2wol_has_competent_anti_air(world, player)), |                                    state._sc2wol_has_competent_anti_air(multiworld, player)), | ||||||
|         LocationData("Safe Haven", "Safe Haven: South Nexus", SC2WOL_LOC_ID_OFFSET + 603, |         LocationData("Safe Haven", "Safe Haven: South Nexus", SC2WOL_LOC_ID_OFFSET + 603, | ||||||
|                      lambda state: state._sc2wol_has_common_unit(world, player) and |                      lambda state: state._sc2wol_has_common_unit(multiworld, player) and | ||||||
|                                    state._sc2wol_has_competent_anti_air(world, player)), |                                    state._sc2wol_has_competent_anti_air(multiworld, player)), | ||||||
|         LocationData("Haven's Fall", "Haven's Fall: Victory", SC2WOL_LOC_ID_OFFSET + 700, |         LocationData("Haven's Fall", "Haven's Fall: Victory", SC2WOL_LOC_ID_OFFSET + 700, | ||||||
|                      lambda state: state._sc2wol_has_common_unit(world, player) and |                      lambda state: state._sc2wol_has_common_unit(multiworld, player) and | ||||||
|                                    state._sc2wol_has_competent_anti_air(world, player) and |                                    state._sc2wol_has_competent_anti_air(multiworld, player) and | ||||||
|                                    state._sc2wol_defense_rating(world, player, True) >= 3), |                                    state._sc2wol_defense_rating(multiworld, player, True) >= 3), | ||||||
|         LocationData("Haven's Fall", "Haven's Fall: North Hive", SC2WOL_LOC_ID_OFFSET + 701, |         LocationData("Haven's Fall", "Haven's Fall: North Hive", SC2WOL_LOC_ID_OFFSET + 701, | ||||||
|                      lambda state: state._sc2wol_has_common_unit(world, player) and |                      lambda state: state._sc2wol_has_common_unit(multiworld, player) and | ||||||
|                                    state._sc2wol_has_competent_anti_air(world, player) and |                                    state._sc2wol_has_competent_anti_air(multiworld, player) and | ||||||
|                                    state._sc2wol_defense_rating(world, player, True) >= 3), |                                    state._sc2wol_defense_rating(multiworld, player, True) >= 3), | ||||||
|         LocationData("Haven's Fall", "Haven's Fall: East Hive", SC2WOL_LOC_ID_OFFSET + 702, |         LocationData("Haven's Fall", "Haven's Fall: East Hive", SC2WOL_LOC_ID_OFFSET + 702, | ||||||
|                      lambda state: state._sc2wol_has_common_unit(world, player) and |                      lambda state: state._sc2wol_has_common_unit(multiworld, player) and | ||||||
|                                    state._sc2wol_has_competent_anti_air(world, player) and |                                    state._sc2wol_has_competent_anti_air(multiworld, player) and | ||||||
|                                    state._sc2wol_defense_rating(world, player, True) >= 3), |                                    state._sc2wol_defense_rating(multiworld, player, True) >= 3), | ||||||
|         LocationData("Haven's Fall", "Haven's Fall: South Hive", SC2WOL_LOC_ID_OFFSET + 703, |         LocationData("Haven's Fall", "Haven's Fall: South Hive", SC2WOL_LOC_ID_OFFSET + 703, | ||||||
|                      lambda state: state._sc2wol_has_common_unit(world, player) and |                      lambda state: state._sc2wol_has_common_unit(multiworld, player) and | ||||||
|                                    state._sc2wol_has_competent_anti_air(world, player) and |                                    state._sc2wol_has_competent_anti_air(multiworld, player) and | ||||||
|                                    state._sc2wol_defense_rating(world, player, True) >= 3), |                                    state._sc2wol_defense_rating(multiworld, player, True) >= 3), | ||||||
|         LocationData("Smash and Grab", "Smash and Grab: Victory", SC2WOL_LOC_ID_OFFSET + 800, |         LocationData("Smash and Grab", "Smash and Grab: Victory", SC2WOL_LOC_ID_OFFSET + 800, | ||||||
|                      lambda state: state._sc2wol_has_common_unit(world, player) and |                      lambda state: state._sc2wol_has_common_unit(multiworld, player) and | ||||||
|                                    (logic_level > 0 and state._sc2wol_has_anti_air(world, player) |                                    (logic_level > 0 and state._sc2wol_has_anti_air(multiworld, player) | ||||||
|                                     or state._sc2wol_has_competent_anti_air(world, player))), |                                     or state._sc2wol_has_competent_anti_air(multiworld, player))), | ||||||
|         LocationData("Smash and Grab", "Smash and Grab: First Relic", SC2WOL_LOC_ID_OFFSET + 801), |         LocationData("Smash and Grab", "Smash and Grab: First Relic", SC2WOL_LOC_ID_OFFSET + 801), | ||||||
|         LocationData("Smash and Grab", "Smash and Grab: Second Relic", SC2WOL_LOC_ID_OFFSET + 802), |         LocationData("Smash and Grab", "Smash and Grab: Second Relic", SC2WOL_LOC_ID_OFFSET + 802), | ||||||
|         LocationData("Smash and Grab", "Smash and Grab: Third Relic", SC2WOL_LOC_ID_OFFSET + 803, |         LocationData("Smash and Grab", "Smash and Grab: Third Relic", SC2WOL_LOC_ID_OFFSET + 803, | ||||||
|                      lambda state: state._sc2wol_has_common_unit(world, player) and |                      lambda state: state._sc2wol_has_common_unit(multiworld, player) and | ||||||
|                                    (logic_level > 0 and state._sc2wol_has_anti_air(world, player) |                                    (logic_level > 0 and state._sc2wol_has_anti_air(multiworld, player) | ||||||
|                                     or state._sc2wol_has_competent_anti_air(world, player))), |                                     or state._sc2wol_has_competent_anti_air(multiworld, player))), | ||||||
|         LocationData("Smash and Grab", "Smash and Grab: Fourth Relic", SC2WOL_LOC_ID_OFFSET + 804, |         LocationData("Smash and Grab", "Smash and Grab: Fourth Relic", SC2WOL_LOC_ID_OFFSET + 804, | ||||||
|                      lambda state: state._sc2wol_has_common_unit(world, player) and |                      lambda state: state._sc2wol_has_common_unit(multiworld, player) and | ||||||
|                                    (logic_level > 0 and state._sc2wol_has_anti_air(world, player) |                                    (logic_level > 0 and state._sc2wol_has_anti_air(multiworld, player) | ||||||
|                                     or state._sc2wol_has_competent_anti_air(world, player))), |                                     or state._sc2wol_has_competent_anti_air(multiworld, player))), | ||||||
|         LocationData("The Dig", "The Dig: Victory", SC2WOL_LOC_ID_OFFSET + 900, |         LocationData("The Dig", "The Dig: Victory", SC2WOL_LOC_ID_OFFSET + 900, | ||||||
|                      lambda state: state._sc2wol_has_anti_air(world, player) and |                      lambda state: state._sc2wol_has_anti_air(multiworld, player) and | ||||||
|                                    state._sc2wol_defense_rating(world, player, False) >= 7), |                                    state._sc2wol_defense_rating(multiworld, player, False) >= 7), | ||||||
|         LocationData("The Dig", "The Dig: Left Relic", SC2WOL_LOC_ID_OFFSET + 901, |         LocationData("The Dig", "The Dig: Left Relic", SC2WOL_LOC_ID_OFFSET + 901, | ||||||
|                      lambda state: state._sc2wol_defense_rating(world, player, False) >= 5), |                      lambda state: state._sc2wol_defense_rating(multiworld, player, False) >= 5), | ||||||
|         LocationData("The Dig", "The Dig: Right Ground Relic", SC2WOL_LOC_ID_OFFSET + 902, |         LocationData("The Dig", "The Dig: Right Ground Relic", SC2WOL_LOC_ID_OFFSET + 902, | ||||||
|                      lambda state: state._sc2wol_defense_rating(world, player, False) >= 5), |                      lambda state: state._sc2wol_defense_rating(multiworld, player, False) >= 5), | ||||||
|         LocationData("The Dig", "The Dig: Right Cliff Relic", SC2WOL_LOC_ID_OFFSET + 903, |         LocationData("The Dig", "The Dig: Right Cliff Relic", SC2WOL_LOC_ID_OFFSET + 903, | ||||||
|                      lambda state: state._sc2wol_defense_rating(world, player, False) >= 5), |                      lambda state: state._sc2wol_defense_rating(multiworld, player, False) >= 5), | ||||||
|         LocationData("The Moebius Factor", "The Moebius Factor: Victory", SC2WOL_LOC_ID_OFFSET + 1000, |         LocationData("The Moebius Factor", "The Moebius Factor: Victory", SC2WOL_LOC_ID_OFFSET + 1000, | ||||||
|                      lambda state: state._sc2wol_has_anti_air(world, player) and |                      lambda state: state._sc2wol_has_anti_air(multiworld, player) and | ||||||
|                                    (state._sc2wol_has_air(world, player) |                                    (state._sc2wol_has_air(multiworld, player) | ||||||
|                                     or state.has_any({'Medivac', 'Hercules'}, player) |                                     or state.has_any({'Medivac', 'Hercules'}, player) | ||||||
|                                     and state._sc2wol_has_common_unit(world, player))), |                                     and state._sc2wol_has_common_unit(multiworld, player))), | ||||||
|         LocationData("The Moebius Factor", "The Moebius Factor: South Rescue", SC2WOL_LOC_ID_OFFSET + 1003, |         LocationData("The Moebius Factor", "The Moebius Factor: South Rescue", SC2WOL_LOC_ID_OFFSET + 1003, | ||||||
|                      lambda state: state._sc2wol_able_to_rescue(world, player)), |                      lambda state: state._sc2wol_able_to_rescue(multiworld, player)), | ||||||
|         LocationData("The Moebius Factor", "The Moebius Factor: Wall Rescue", SC2WOL_LOC_ID_OFFSET + 1004, |         LocationData("The Moebius Factor", "The Moebius Factor: Wall Rescue", SC2WOL_LOC_ID_OFFSET + 1004, | ||||||
|                      lambda state: state._sc2wol_able_to_rescue(world, player)), |                      lambda state: state._sc2wol_able_to_rescue(multiworld, player)), | ||||||
|         LocationData("The Moebius Factor", "The Moebius Factor: Mid Rescue", SC2WOL_LOC_ID_OFFSET + 1005, |         LocationData("The Moebius Factor", "The Moebius Factor: Mid Rescue", SC2WOL_LOC_ID_OFFSET + 1005, | ||||||
|                      lambda state: state._sc2wol_able_to_rescue(world, player)), |                      lambda state: state._sc2wol_able_to_rescue(multiworld, player)), | ||||||
|         LocationData("The Moebius Factor", "The Moebius Factor: Nydus Roof Rescue", SC2WOL_LOC_ID_OFFSET + 1006, |         LocationData("The Moebius Factor", "The Moebius Factor: Nydus Roof Rescue", SC2WOL_LOC_ID_OFFSET + 1006, | ||||||
|                      lambda state: state._sc2wol_able_to_rescue(world, player)), |                      lambda state: state._sc2wol_able_to_rescue(multiworld, player)), | ||||||
|         LocationData("The Moebius Factor", "The Moebius Factor: Alive Inside Rescue", SC2WOL_LOC_ID_OFFSET + 1007, |         LocationData("The Moebius Factor", "The Moebius Factor: Alive Inside Rescue", SC2WOL_LOC_ID_OFFSET + 1007, | ||||||
|                      lambda state: state._sc2wol_able_to_rescue(world, player)), |                      lambda state: state._sc2wol_able_to_rescue(multiworld, player)), | ||||||
|         LocationData("The Moebius Factor", "The Moebius Factor: Brutalisk", SC2WOL_LOC_ID_OFFSET + 1008, |         LocationData("The Moebius Factor", "The Moebius Factor: Brutalisk", SC2WOL_LOC_ID_OFFSET + 1008, | ||||||
|                      lambda state: state._sc2wol_has_anti_air(world, player) and |                      lambda state: state._sc2wol_has_anti_air(multiworld, player) and | ||||||
|                                    (state._sc2wol_has_air(world, player) |                                    (state._sc2wol_has_air(multiworld, player) | ||||||
|                                     or state.has_any({'Medivac', 'Hercules'}, player) |                                     or state.has_any({'Medivac', 'Hercules'}, player) | ||||||
|                                     and state._sc2wol_has_common_unit(world, player))), |                                     and state._sc2wol_has_common_unit(multiworld, player))), | ||||||
|         LocationData("Supernova", "Supernova: Victory", SC2WOL_LOC_ID_OFFSET + 1100, |         LocationData("Supernova", "Supernova: Victory", SC2WOL_LOC_ID_OFFSET + 1100, | ||||||
|                      lambda state: state._sc2wol_beats_protoss_deathball(world, player)), |                      lambda state: state._sc2wol_beats_protoss_deathball(multiworld, player)), | ||||||
|         LocationData("Supernova", "Supernova: West Relic", SC2WOL_LOC_ID_OFFSET + 1101), |         LocationData("Supernova", "Supernova: West Relic", SC2WOL_LOC_ID_OFFSET + 1101), | ||||||
|         LocationData("Supernova", "Supernova: North Relic", SC2WOL_LOC_ID_OFFSET + 1102), |         LocationData("Supernova", "Supernova: North Relic", SC2WOL_LOC_ID_OFFSET + 1102), | ||||||
|         LocationData("Supernova", "Supernova: South Relic", SC2WOL_LOC_ID_OFFSET + 1103, |         LocationData("Supernova", "Supernova: South Relic", SC2WOL_LOC_ID_OFFSET + 1103, | ||||||
|                      lambda state: state._sc2wol_beats_protoss_deathball(world, player)), |                      lambda state: state._sc2wol_beats_protoss_deathball(multiworld, player)), | ||||||
|         LocationData("Supernova", "Supernova: East Relic", SC2WOL_LOC_ID_OFFSET + 1104, |         LocationData("Supernova", "Supernova: East Relic", SC2WOL_LOC_ID_OFFSET + 1104, | ||||||
|                      lambda state: state._sc2wol_beats_protoss_deathball(world, player)), |                      lambda state: state._sc2wol_beats_protoss_deathball(multiworld, player)), | ||||||
|         LocationData("Maw of the Void", "Maw of the Void: Victory", SC2WOL_LOC_ID_OFFSET + 1200, |         LocationData("Maw of the Void", "Maw of the Void: Victory", SC2WOL_LOC_ID_OFFSET + 1200, | ||||||
|                      lambda state: state._sc2wol_survives_rip_field(world, player)), |                      lambda state: state._sc2wol_survives_rip_field(multiworld, player)), | ||||||
|         LocationData("Maw of the Void", "Maw of the Void: Landing Zone Cleared", SC2WOL_LOC_ID_OFFSET + 1201), |         LocationData("Maw of the Void", "Maw of the Void: Landing Zone Cleared", SC2WOL_LOC_ID_OFFSET + 1201), | ||||||
|         LocationData("Maw of the Void", "Maw of the Void: Expansion Prisoners", SC2WOL_LOC_ID_OFFSET + 1202, |         LocationData("Maw of the Void", "Maw of the Void: Expansion Prisoners", SC2WOL_LOC_ID_OFFSET + 1202, | ||||||
|                      lambda state: logic_level > 0 or state._sc2wol_survives_rip_field(world, player)), |                      lambda state: logic_level > 0 or state._sc2wol_survives_rip_field(multiworld, player)), | ||||||
|         LocationData("Maw of the Void", "Maw of the Void: South Close Prisoners", SC2WOL_LOC_ID_OFFSET + 1203, |         LocationData("Maw of the Void", "Maw of the Void: South Close Prisoners", SC2WOL_LOC_ID_OFFSET + 1203, | ||||||
|                      lambda state: logic_level > 0 or state._sc2wol_survives_rip_field(world, player)), |                      lambda state: logic_level > 0 or state._sc2wol_survives_rip_field(multiworld, player)), | ||||||
|         LocationData("Maw of the Void", "Maw of the Void: South Far Prisoners", SC2WOL_LOC_ID_OFFSET + 1204, |         LocationData("Maw of the Void", "Maw of the Void: South Far Prisoners", SC2WOL_LOC_ID_OFFSET + 1204, | ||||||
|                      lambda state: state._sc2wol_survives_rip_field(world, player)), |                      lambda state: state._sc2wol_survives_rip_field(multiworld, player)), | ||||||
|         LocationData("Maw of the Void", "Maw of the Void: North Prisoners", SC2WOL_LOC_ID_OFFSET + 1205, |         LocationData("Maw of the Void", "Maw of the Void: North Prisoners", SC2WOL_LOC_ID_OFFSET + 1205, | ||||||
|                      lambda state: state._sc2wol_survives_rip_field(world, player)), |                      lambda state: state._sc2wol_survives_rip_field(multiworld, player)), | ||||||
|         LocationData("Devil's Playground", "Devil's Playground: Victory", SC2WOL_LOC_ID_OFFSET + 1300, |         LocationData("Devil's Playground", "Devil's Playground: Victory", SC2WOL_LOC_ID_OFFSET + 1300, | ||||||
|                      lambda state: logic_level > 0 or |                      lambda state: logic_level > 0 or | ||||||
|                                    state._sc2wol_has_anti_air(world, player) and ( |                                    state._sc2wol_has_anti_air(multiworld, player) and ( | ||||||
|                                    state._sc2wol_has_common_unit(world, player) or state.has("Reaper", player))), |                                            state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player))), | ||||||
|         LocationData("Devil's Playground", "Devil's Playground: Tosh's Miners", SC2WOL_LOC_ID_OFFSET + 1301), |         LocationData("Devil's Playground", "Devil's Playground: Tosh's Miners", SC2WOL_LOC_ID_OFFSET + 1301), | ||||||
|         LocationData("Devil's Playground", "Devil's Playground: Brutalisk", SC2WOL_LOC_ID_OFFSET + 1302, |         LocationData("Devil's Playground", "Devil's Playground: Brutalisk", SC2WOL_LOC_ID_OFFSET + 1302, | ||||||
|                      lambda state: logic_level > 0 or state._sc2wol_has_common_unit(world, player) or state.has("Reaper", player)), |                      lambda state: logic_level > 0 or state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player)), | ||||||
|         LocationData("Welcome to the Jungle", "Welcome to the Jungle: Victory", SC2WOL_LOC_ID_OFFSET + 1400, |         LocationData("Welcome to the Jungle", "Welcome to the Jungle: Victory", SC2WOL_LOC_ID_OFFSET + 1400, | ||||||
|                      lambda state: state._sc2wol_has_common_unit(world, player) and |                      lambda state: state._sc2wol_has_common_unit(multiworld, player) and | ||||||
|                                    state._sc2wol_has_competent_anti_air(world, player)), |                                    state._sc2wol_has_competent_anti_air(multiworld, player)), | ||||||
|         LocationData("Welcome to the Jungle", "Welcome to the Jungle: Close Relic", SC2WOL_LOC_ID_OFFSET + 1401), |         LocationData("Welcome to the Jungle", "Welcome to the Jungle: Close Relic", SC2WOL_LOC_ID_OFFSET + 1401), | ||||||
|         LocationData("Welcome to the Jungle", "Welcome to the Jungle: West Relic", SC2WOL_LOC_ID_OFFSET + 1402, |         LocationData("Welcome to the Jungle", "Welcome to the Jungle: West Relic", SC2WOL_LOC_ID_OFFSET + 1402, | ||||||
|                      lambda state: state._sc2wol_has_common_unit(world, player) and |                      lambda state: state._sc2wol_has_common_unit(multiworld, player) and | ||||||
|                                    state._sc2wol_has_competent_anti_air(world, player)), |                                    state._sc2wol_has_competent_anti_air(multiworld, player)), | ||||||
|         LocationData("Welcome to the Jungle", "Welcome to the Jungle: North-East Relic", SC2WOL_LOC_ID_OFFSET + 1403, |         LocationData("Welcome to the Jungle", "Welcome to the Jungle: North-East Relic", SC2WOL_LOC_ID_OFFSET + 1403, | ||||||
|                      lambda state: state._sc2wol_has_common_unit(world, player) and |                      lambda state: state._sc2wol_has_common_unit(multiworld, player) and | ||||||
|                                    state._sc2wol_has_competent_anti_air(world, player)), |                                    state._sc2wol_has_competent_anti_air(multiworld, player)), | ||||||
|         LocationData("Breakout", "Breakout: Victory", SC2WOL_LOC_ID_OFFSET + 1500), |         LocationData("Breakout", "Breakout: Victory", SC2WOL_LOC_ID_OFFSET + 1500), | ||||||
|         LocationData("Breakout", "Breakout: Diamondback Prison", SC2WOL_LOC_ID_OFFSET + 1501), |         LocationData("Breakout", "Breakout: Diamondback Prison", SC2WOL_LOC_ID_OFFSET + 1501), | ||||||
|         LocationData("Breakout", "Breakout: Siegetank Prison", SC2WOL_LOC_ID_OFFSET + 1502), |         LocationData("Breakout", "Breakout: Siegetank Prison", SC2WOL_LOC_ID_OFFSET + 1502), | ||||||
|  | @ -178,101 +178,101 @@ def get_locations(world: Optional[MultiWorld], player: Optional[int]) -> Tuple[L | ||||||
|         LocationData("Ghost of a Chance", "Ghost of a Chance: Second Island Spectres", SC2WOL_LOC_ID_OFFSET + 1604), |         LocationData("Ghost of a Chance", "Ghost of a Chance: Second Island Spectres", SC2WOL_LOC_ID_OFFSET + 1604), | ||||||
|         LocationData("Ghost of a Chance", "Ghost of a Chance: Third Island Spectres", SC2WOL_LOC_ID_OFFSET + 1605), |         LocationData("Ghost of a Chance", "Ghost of a Chance: Third Island Spectres", SC2WOL_LOC_ID_OFFSET + 1605), | ||||||
|         LocationData("The Great Train Robbery", "The Great Train Robbery: Victory", SC2WOL_LOC_ID_OFFSET + 1700, |         LocationData("The Great Train Robbery", "The Great Train Robbery: Victory", SC2WOL_LOC_ID_OFFSET + 1700, | ||||||
|                      lambda state: state._sc2wol_has_train_killers(world, player) and |                      lambda state: state._sc2wol_has_train_killers(multiworld, player) and | ||||||
|                                    state._sc2wol_has_anti_air(world, player)), |                                    state._sc2wol_has_anti_air(multiworld, player)), | ||||||
|         LocationData("The Great Train Robbery", "The Great Train Robbery: North Defiler", SC2WOL_LOC_ID_OFFSET + 1701), |         LocationData("The Great Train Robbery", "The Great Train Robbery: North Defiler", SC2WOL_LOC_ID_OFFSET + 1701), | ||||||
|         LocationData("The Great Train Robbery", "The Great Train Robbery: Mid Defiler", SC2WOL_LOC_ID_OFFSET + 1702), |         LocationData("The Great Train Robbery", "The Great Train Robbery: Mid Defiler", SC2WOL_LOC_ID_OFFSET + 1702), | ||||||
|         LocationData("The Great Train Robbery", "The Great Train Robbery: South Defiler", SC2WOL_LOC_ID_OFFSET + 1703), |         LocationData("The Great Train Robbery", "The Great Train Robbery: South Defiler", SC2WOL_LOC_ID_OFFSET + 1703), | ||||||
|         LocationData("Cutthroat", "Cutthroat: Victory", SC2WOL_LOC_ID_OFFSET + 1800, |         LocationData("Cutthroat", "Cutthroat: Victory", SC2WOL_LOC_ID_OFFSET + 1800, | ||||||
|                      lambda state: state._sc2wol_has_common_unit(world, player) and |                      lambda state: state._sc2wol_has_common_unit(multiworld, player) and | ||||||
|                                    (logic_level > 0 or state._sc2wol_has_anti_air)), |                                    (logic_level > 0 or state._sc2wol_has_anti_air)), | ||||||
|         LocationData("Cutthroat", "Cutthroat: Mira Han", SC2WOL_LOC_ID_OFFSET + 1801, |         LocationData("Cutthroat", "Cutthroat: Mira Han", SC2WOL_LOC_ID_OFFSET + 1801, | ||||||
|                      lambda state: state._sc2wol_has_common_unit(world, player)), |                      lambda state: state._sc2wol_has_common_unit(multiworld, player)), | ||||||
|         LocationData("Cutthroat", "Cutthroat: North Relic", SC2WOL_LOC_ID_OFFSET + 1802, |         LocationData("Cutthroat", "Cutthroat: North Relic", SC2WOL_LOC_ID_OFFSET + 1802, | ||||||
|                      lambda state: state._sc2wol_has_common_unit(world, player)), |                      lambda state: state._sc2wol_has_common_unit(multiworld, player)), | ||||||
|         LocationData("Cutthroat", "Cutthroat: Mid Relic", SC2WOL_LOC_ID_OFFSET + 1803), |         LocationData("Cutthroat", "Cutthroat: Mid Relic", SC2WOL_LOC_ID_OFFSET + 1803), | ||||||
|         LocationData("Cutthroat", "Cutthroat: Southwest Relic", SC2WOL_LOC_ID_OFFSET + 1804, |         LocationData("Cutthroat", "Cutthroat: Southwest Relic", SC2WOL_LOC_ID_OFFSET + 1804, | ||||||
|                      lambda state: state._sc2wol_has_common_unit(world, player)), |                      lambda state: state._sc2wol_has_common_unit(multiworld, player)), | ||||||
|         LocationData("Engine of Destruction", "Engine of Destruction: Victory", SC2WOL_LOC_ID_OFFSET + 1900, |         LocationData("Engine of Destruction", "Engine of Destruction: Victory", SC2WOL_LOC_ID_OFFSET + 1900, | ||||||
|                      lambda state: state._sc2wol_has_competent_anti_air(world, player) and |                      lambda state: state._sc2wol_has_competent_anti_air(multiworld, player) and | ||||||
|                                    state._sc2wol_has_common_unit(world, player) or state.has('Wraith', player)), |                                    state._sc2wol_has_common_unit(multiworld, player) or state.has('Wraith', player)), | ||||||
|         LocationData("Engine of Destruction", "Engine of Destruction: Odin", SC2WOL_LOC_ID_OFFSET + 1901), |         LocationData("Engine of Destruction", "Engine of Destruction: Odin", SC2WOL_LOC_ID_OFFSET + 1901), | ||||||
|         LocationData("Engine of Destruction", "Engine of Destruction: Loki", SC2WOL_LOC_ID_OFFSET + 1902, |         LocationData("Engine of Destruction", "Engine of Destruction: Loki", SC2WOL_LOC_ID_OFFSET + 1902, | ||||||
|                      lambda state: state._sc2wol_has_competent_anti_air(world, player) and |                      lambda state: state._sc2wol_has_competent_anti_air(multiworld, player) and | ||||||
|                                    state._sc2wol_has_common_unit(world, player) or state.has('Wraith', player)), |                                    state._sc2wol_has_common_unit(multiworld, player) or state.has('Wraith', player)), | ||||||
|         LocationData("Engine of Destruction", "Engine of Destruction: Lab Devourer", SC2WOL_LOC_ID_OFFSET + 1903), |         LocationData("Engine of Destruction", "Engine of Destruction: Lab Devourer", SC2WOL_LOC_ID_OFFSET + 1903), | ||||||
|         LocationData("Engine of Destruction", "Engine of Destruction: North Devourer", SC2WOL_LOC_ID_OFFSET + 1904, |         LocationData("Engine of Destruction", "Engine of Destruction: North Devourer", SC2WOL_LOC_ID_OFFSET + 1904, | ||||||
|                      lambda state: state._sc2wol_has_competent_anti_air(world, player) and |                      lambda state: state._sc2wol_has_competent_anti_air(multiworld, player) and | ||||||
|                                    state._sc2wol_has_common_unit(world, player) or state.has('Wraith', player)), |                                    state._sc2wol_has_common_unit(multiworld, player) or state.has('Wraith', player)), | ||||||
|         LocationData("Engine of Destruction", "Engine of Destruction: Southeast Devourer", SC2WOL_LOC_ID_OFFSET + 1905, |         LocationData("Engine of Destruction", "Engine of Destruction: Southeast Devourer", SC2WOL_LOC_ID_OFFSET + 1905, | ||||||
|                      lambda state: state._sc2wol_has_competent_anti_air(world, player) and |                      lambda state: state._sc2wol_has_competent_anti_air(multiworld, player) and | ||||||
|                                    state._sc2wol_has_common_unit(world, player) or state.has('Wraith', player)), |                                    state._sc2wol_has_common_unit(multiworld, player) or state.has('Wraith', player)), | ||||||
|         LocationData("Media Blitz", "Media Blitz: Victory", SC2WOL_LOC_ID_OFFSET + 2000, |         LocationData("Media Blitz", "Media Blitz: Victory", SC2WOL_LOC_ID_OFFSET + 2000, | ||||||
|                      lambda state: state._sc2wol_has_competent_comp(world, player)), |                      lambda state: state._sc2wol_has_competent_comp(multiworld, player)), | ||||||
|         LocationData("Media Blitz", "Media Blitz: Tower 1", SC2WOL_LOC_ID_OFFSET + 2001, |         LocationData("Media Blitz", "Media Blitz: Tower 1", SC2WOL_LOC_ID_OFFSET + 2001, | ||||||
|                      lambda state: state._sc2wol_has_competent_comp(world, player)), |                      lambda state: state._sc2wol_has_competent_comp(multiworld, player)), | ||||||
|         LocationData("Media Blitz", "Media Blitz: Tower 2", SC2WOL_LOC_ID_OFFSET + 2002, |         LocationData("Media Blitz", "Media Blitz: Tower 2", SC2WOL_LOC_ID_OFFSET + 2002, | ||||||
|                      lambda state: state._sc2wol_has_competent_comp(world, player)), |                      lambda state: state._sc2wol_has_competent_comp(multiworld, player)), | ||||||
|         LocationData("Media Blitz", "Media Blitz: Tower 3", SC2WOL_LOC_ID_OFFSET + 2003, |         LocationData("Media Blitz", "Media Blitz: Tower 3", SC2WOL_LOC_ID_OFFSET + 2003, | ||||||
|                      lambda state: state._sc2wol_has_competent_comp(world, player)), |                      lambda state: state._sc2wol_has_competent_comp(multiworld, player)), | ||||||
|         LocationData("Media Blitz", "Media Blitz: Science Facility", SC2WOL_LOC_ID_OFFSET + 2004), |         LocationData("Media Blitz", "Media Blitz: Science Facility", SC2WOL_LOC_ID_OFFSET + 2004), | ||||||
|         LocationData("Piercing the Shroud", "Piercing the Shroud: Victory", SC2WOL_LOC_ID_OFFSET + 2100, |         LocationData("Piercing the Shroud", "Piercing the Shroud: Victory", SC2WOL_LOC_ID_OFFSET + 2100, | ||||||
|                      lambda state: state._sc2wol_has_mm_upgrade(world, player)), |                      lambda state: state._sc2wol_has_mm_upgrade(multiworld, player)), | ||||||
|         LocationData("Piercing the Shroud", "Piercing the Shroud: Holding Cell Relic", SC2WOL_LOC_ID_OFFSET + 2101), |         LocationData("Piercing the Shroud", "Piercing the Shroud: Holding Cell Relic", SC2WOL_LOC_ID_OFFSET + 2101), | ||||||
|         LocationData("Piercing the Shroud", "Piercing the Shroud: Brutalisk Relic", SC2WOL_LOC_ID_OFFSET + 2102, |         LocationData("Piercing the Shroud", "Piercing the Shroud: Brutalisk Relic", SC2WOL_LOC_ID_OFFSET + 2102, | ||||||
|                      lambda state: state._sc2wol_has_mm_upgrade(world, player)), |                      lambda state: state._sc2wol_has_mm_upgrade(multiworld, player)), | ||||||
|         LocationData("Piercing the Shroud", "Piercing the Shroud: First Escape Relic", SC2WOL_LOC_ID_OFFSET + 2103, |         LocationData("Piercing the Shroud", "Piercing the Shroud: First Escape Relic", SC2WOL_LOC_ID_OFFSET + 2103, | ||||||
|                      lambda state: state._sc2wol_has_mm_upgrade(world, player)), |                      lambda state: state._sc2wol_has_mm_upgrade(multiworld, player)), | ||||||
|         LocationData("Piercing the Shroud", "Piercing the Shroud: Second Escape Relic", SC2WOL_LOC_ID_OFFSET + 2104, |         LocationData("Piercing the Shroud", "Piercing the Shroud: Second Escape Relic", SC2WOL_LOC_ID_OFFSET + 2104, | ||||||
|                      lambda state: state._sc2wol_has_mm_upgrade(world, player)), |                      lambda state: state._sc2wol_has_mm_upgrade(multiworld, player)), | ||||||
|         LocationData("Piercing the Shroud", "Piercing the Shroud: Brutalisk ", SC2WOL_LOC_ID_OFFSET + 2105, |         LocationData("Piercing the Shroud", "Piercing the Shroud: Brutalisk ", SC2WOL_LOC_ID_OFFSET + 2105, | ||||||
|                      lambda state: state._sc2wol_has_mm_upgrade(world, player)), |                      lambda state: state._sc2wol_has_mm_upgrade(multiworld, player)), | ||||||
|         LocationData("Whispers of Doom", "Whispers of Doom: Victory", SC2WOL_LOC_ID_OFFSET + 2200), |         LocationData("Whispers of Doom", "Whispers of Doom: Victory", SC2WOL_LOC_ID_OFFSET + 2200), | ||||||
|         LocationData("Whispers of Doom", "Whispers of Doom: First Hatchery", SC2WOL_LOC_ID_OFFSET + 2201), |         LocationData("Whispers of Doom", "Whispers of Doom: First Hatchery", SC2WOL_LOC_ID_OFFSET + 2201), | ||||||
|         LocationData("Whispers of Doom", "Whispers of Doom: Second Hatchery", SC2WOL_LOC_ID_OFFSET + 2202), |         LocationData("Whispers of Doom", "Whispers of Doom: Second Hatchery", SC2WOL_LOC_ID_OFFSET + 2202), | ||||||
|         LocationData("Whispers of Doom", "Whispers of Doom: Third Hatchery", SC2WOL_LOC_ID_OFFSET + 2203), |         LocationData("Whispers of Doom", "Whispers of Doom: Third Hatchery", SC2WOL_LOC_ID_OFFSET + 2203), | ||||||
|         LocationData("A Sinister Turn", "A Sinister Turn: Victory", SC2WOL_LOC_ID_OFFSET + 2300, |         LocationData("A Sinister Turn", "A Sinister Turn: Victory", SC2WOL_LOC_ID_OFFSET + 2300, | ||||||
|                      lambda state: state._sc2wol_has_protoss_medium_units(world, player)), |                      lambda state: state._sc2wol_has_protoss_medium_units(multiworld, player)), | ||||||
|         LocationData("A Sinister Turn", "A Sinister Turn: Robotics Facility", SC2WOL_LOC_ID_OFFSET + 2301, |         LocationData("A Sinister Turn", "A Sinister Turn: Robotics Facility", SC2WOL_LOC_ID_OFFSET + 2301, | ||||||
|                      lambda state: logic_level > 0 or state._sc2wol_has_protoss_common_units(world, player)), |                      lambda state: logic_level > 0 or state._sc2wol_has_protoss_common_units(multiworld, player)), | ||||||
|         LocationData("A Sinister Turn", "A Sinister Turn: Dark Shrine", SC2WOL_LOC_ID_OFFSET + 2302, |         LocationData("A Sinister Turn", "A Sinister Turn: Dark Shrine", SC2WOL_LOC_ID_OFFSET + 2302, | ||||||
|                      lambda state: logic_level > 0 or state._sc2wol_has_protoss_common_units(world, player)), |                      lambda state: logic_level > 0 or state._sc2wol_has_protoss_common_units(multiworld, player)), | ||||||
|         LocationData("A Sinister Turn", "A Sinister Turn: Templar Archives", SC2WOL_LOC_ID_OFFSET + 2303, |         LocationData("A Sinister Turn", "A Sinister Turn: Templar Archives", SC2WOL_LOC_ID_OFFSET + 2303, | ||||||
|                      lambda state: state._sc2wol_has_protoss_common_units(world, player)), |                      lambda state: state._sc2wol_has_protoss_common_units(multiworld, player)), | ||||||
|         LocationData("Echoes of the Future", "Echoes of the Future: Victory", SC2WOL_LOC_ID_OFFSET + 2400, |         LocationData("Echoes of the Future", "Echoes of the Future: Victory", SC2WOL_LOC_ID_OFFSET + 2400, | ||||||
|                      lambda state: logic_level > 0 or state._sc2wol_has_protoss_medium_units(world, player)), |                      lambda state: logic_level > 0 or state._sc2wol_has_protoss_medium_units(multiworld, player)), | ||||||
|         LocationData("Echoes of the Future", "Echoes of the Future: Close Obelisk", SC2WOL_LOC_ID_OFFSET + 2401), |         LocationData("Echoes of the Future", "Echoes of the Future: Close Obelisk", SC2WOL_LOC_ID_OFFSET + 2401), | ||||||
|         LocationData("Echoes of the Future", "Echoes of the Future: West Obelisk", SC2WOL_LOC_ID_OFFSET + 2402, |         LocationData("Echoes of the Future", "Echoes of the Future: West Obelisk", SC2WOL_LOC_ID_OFFSET + 2402, | ||||||
|                      lambda state: logic_level > 0 or state._sc2wol_has_protoss_common_units(world, player)), |                      lambda state: logic_level > 0 or state._sc2wol_has_protoss_common_units(multiworld, player)), | ||||||
|         LocationData("In Utter Darkness", "In Utter Darkness: Defeat", SC2WOL_LOC_ID_OFFSET + 2500), |         LocationData("In Utter Darkness", "In Utter Darkness: Defeat", SC2WOL_LOC_ID_OFFSET + 2500), | ||||||
|         LocationData("In Utter Darkness", "In Utter Darkness: Protoss Archive", SC2WOL_LOC_ID_OFFSET + 2501, |         LocationData("In Utter Darkness", "In Utter Darkness: Protoss Archive", SC2WOL_LOC_ID_OFFSET + 2501, | ||||||
|                      lambda state: state._sc2wol_has_protoss_medium_units(world, player)), |                      lambda state: state._sc2wol_has_protoss_medium_units(multiworld, player)), | ||||||
|         LocationData("In Utter Darkness", "In Utter Darkness: Kills", SC2WOL_LOC_ID_OFFSET + 2502, |         LocationData("In Utter Darkness", "In Utter Darkness: Kills", SC2WOL_LOC_ID_OFFSET + 2502, | ||||||
|                      lambda state: state._sc2wol_has_protoss_common_units(world, player)), |                      lambda state: state._sc2wol_has_protoss_common_units(multiworld, player)), | ||||||
|         LocationData("Gates of Hell", "Gates of Hell: Victory", SC2WOL_LOC_ID_OFFSET + 2600, |         LocationData("Gates of Hell", "Gates of Hell: Victory", SC2WOL_LOC_ID_OFFSET + 2600, | ||||||
|                      lambda state: state._sc2wol_has_competent_comp(world, player) and |                      lambda state: state._sc2wol_has_competent_comp(multiworld, player) and | ||||||
|                                    state._sc2wol_defense_rating(world, player, True) > 6), |                                    state._sc2wol_defense_rating(multiworld, player, True) > 6), | ||||||
|         LocationData("Gates of Hell", "Gates of Hell: Large Army", SC2WOL_LOC_ID_OFFSET + 2601, |         LocationData("Gates of Hell", "Gates of Hell: Large Army", SC2WOL_LOC_ID_OFFSET + 2601, | ||||||
|                      lambda state: state._sc2wol_has_competent_comp(world, player) and |                      lambda state: state._sc2wol_has_competent_comp(multiworld, player) and | ||||||
|                                    state._sc2wol_defense_rating(world, player, True) > 6), |                                    state._sc2wol_defense_rating(multiworld, player, True) > 6), | ||||||
|         LocationData("Belly of the Beast", "Belly of the Beast: Victory", SC2WOL_LOC_ID_OFFSET + 2700), |         LocationData("Belly of the Beast", "Belly of the Beast: Victory", SC2WOL_LOC_ID_OFFSET + 2700), | ||||||
|         LocationData("Belly of the Beast", "Belly of the Beast: First Charge", SC2WOL_LOC_ID_OFFSET + 2701), |         LocationData("Belly of the Beast", "Belly of the Beast: First Charge", SC2WOL_LOC_ID_OFFSET + 2701), | ||||||
|         LocationData("Belly of the Beast", "Belly of the Beast: Second Charge", SC2WOL_LOC_ID_OFFSET + 2702), |         LocationData("Belly of the Beast", "Belly of the Beast: Second Charge", SC2WOL_LOC_ID_OFFSET + 2702), | ||||||
|         LocationData("Belly of the Beast", "Belly of the Beast: Third Charge", SC2WOL_LOC_ID_OFFSET + 2703), |         LocationData("Belly of the Beast", "Belly of the Beast: Third Charge", SC2WOL_LOC_ID_OFFSET + 2703), | ||||||
|         LocationData("Shatter the Sky", "Shatter the Sky: Victory", SC2WOL_LOC_ID_OFFSET + 2800, |         LocationData("Shatter the Sky", "Shatter the Sky: Victory", SC2WOL_LOC_ID_OFFSET + 2800, | ||||||
|                      lambda state: state._sc2wol_has_competent_comp(world, player)), |                      lambda state: state._sc2wol_has_competent_comp(multiworld, player)), | ||||||
|         LocationData("Shatter the Sky", "Shatter the Sky: Close Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2801, |         LocationData("Shatter the Sky", "Shatter the Sky: Close Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2801, | ||||||
|                      lambda state: state._sc2wol_has_competent_comp(world, player)), |                      lambda state: state._sc2wol_has_competent_comp(multiworld, player)), | ||||||
|         LocationData("Shatter the Sky", "Shatter the Sky: Northwest Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2802, |         LocationData("Shatter the Sky", "Shatter the Sky: Northwest Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2802, | ||||||
|                      lambda state: state._sc2wol_has_competent_comp(world, player)), |                      lambda state: state._sc2wol_has_competent_comp(multiworld, player)), | ||||||
|         LocationData("Shatter the Sky", "Shatter the Sky: Southeast Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2803, |         LocationData("Shatter the Sky", "Shatter the Sky: Southeast Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2803, | ||||||
|                      lambda state: state._sc2wol_has_competent_comp(world, player)), |                      lambda state: state._sc2wol_has_competent_comp(multiworld, player)), | ||||||
|         LocationData("Shatter the Sky", "Shatter the Sky: Southwest Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2804, |         LocationData("Shatter the Sky", "Shatter the Sky: Southwest Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2804, | ||||||
|                      lambda state: state._sc2wol_has_competent_comp(world, player)), |                      lambda state: state._sc2wol_has_competent_comp(multiworld, player)), | ||||||
|         LocationData("Shatter the Sky", "Shatter the Sky: Leviathan", SC2WOL_LOC_ID_OFFSET + 2805, |         LocationData("Shatter the Sky", "Shatter the Sky: Leviathan", SC2WOL_LOC_ID_OFFSET + 2805, | ||||||
|                      lambda state: state._sc2wol_has_competent_comp(world, player)), |                      lambda state: state._sc2wol_has_competent_comp(multiworld, player)), | ||||||
|         LocationData("All-In", "All-In: Victory", None, |         LocationData("All-In", "All-In: Victory", None, | ||||||
|                      lambda state: state._sc2wol_final_mission_requirements(world, player)) |                      lambda state: state._sc2wol_final_mission_requirements(multiworld, player)) | ||||||
|     ] |     ] | ||||||
| 
 | 
 | ||||||
|     beat_events = [] |     beat_events = [] | ||||||
|  | @ -280,7 +280,8 @@ def get_locations(world: Optional[MultiWorld], player: Optional[int]) -> Tuple[L | ||||||
|     for i, location_data in enumerate(location_table): |     for i, location_data in enumerate(location_table): | ||||||
|         # Removing all item-based logic on No Logic |         # Removing all item-based logic on No Logic | ||||||
|         if logic_level == 2: |         if logic_level == 2: | ||||||
|             location_table[i] = location_data._replace(rule=Location.access_rule) |             location_data = location_data._replace(rule=Location.access_rule) | ||||||
|  |             location_table[i] = location_data | ||||||
|         # Generating Beat event locations |         # Generating Beat event locations | ||||||
|         if location_data.name.endswith((": Victory", ": Defeat")): |         if location_data.name.endswith((": Victory", ": Defeat")): | ||||||
|             beat_events.append( |             beat_events.append( | ||||||
|  |  | ||||||
|  | @ -5,26 +5,26 @@ from .Items import get_basic_units, defense_ratings, zerg_defense_ratings | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class SC2WoLLogic(LogicMixin): | class SC2WoLLogic(LogicMixin): | ||||||
|     def _sc2wol_has_common_unit(self, world: MultiWorld, player: int) -> bool: |     def _sc2wol_has_common_unit(self, multiworld: MultiWorld, player: int) -> bool: | ||||||
|         return self.has_any(get_basic_units(world, player), player) |         return self.has_any(get_basic_units(multiworld, player), player) | ||||||
| 
 | 
 | ||||||
|     def _sc2wol_has_air(self, world: MultiWorld, player: int) -> bool: |     def _sc2wol_has_air(self, multiworld: MultiWorld, player: int) -> bool: | ||||||
|         return self.has_any({'Viking', 'Wraith', 'Banshee'}, player) or get_option_value(world, player, 'required_tactics') > 0 \ |         return self.has_any({'Viking', 'Wraith', 'Banshee'}, player) or get_option_value(multiworld, player, 'required_tactics') > 0 \ | ||||||
|                 and self.has_any({'Hercules', 'Medivac'}, player) and self._sc2wol_has_common_unit(world, player) |                 and self.has_any({'Hercules', 'Medivac'}, player) and self._sc2wol_has_common_unit(multiworld, player) | ||||||
| 
 | 
 | ||||||
|     def _sc2wol_has_air_anti_air(self, world: MultiWorld, player: int) -> bool: |     def _sc2wol_has_air_anti_air(self, multiworld: MultiWorld, player: int) -> bool: | ||||||
|         return self.has('Viking', player) \ |         return self.has('Viking', player) \ | ||||||
|                or get_option_value(world, player, 'required_tactics') > 0 and self.has('Wraith', player) |                or get_option_value(multiworld, player, 'required_tactics') > 0 and self.has('Wraith', player) | ||||||
| 
 | 
 | ||||||
|     def _sc2wol_has_competent_anti_air(self, world: MultiWorld, player: int) -> bool: |     def _sc2wol_has_competent_anti_air(self, multiworld: MultiWorld, player: int) -> bool: | ||||||
|         return self.has_any({'Marine', 'Goliath'}, player) or self._sc2wol_has_air_anti_air(world, player) |         return self.has_any({'Marine', 'Goliath'}, player) or self._sc2wol_has_air_anti_air(multiworld, player) | ||||||
| 
 | 
 | ||||||
|     def _sc2wol_has_anti_air(self, world: MultiWorld, player: int) -> bool: |     def _sc2wol_has_anti_air(self, multiworld: MultiWorld, player: int) -> bool: | ||||||
|         return self.has_any({'Missile Turret', 'Thor', 'War Pigs', 'Spartan Company', "Hel's Angel", 'Battlecruiser', 'Wraith'}, player) \ |         return self.has_any({'Missile Turret', 'Thor', 'War Pigs', 'Spartan Company', "Hel's Angel", 'Battlecruiser', 'Wraith'}, player) \ | ||||||
|                 or self._sc2wol_has_competent_anti_air(world, player) \ |                 or self._sc2wol_has_competent_anti_air(multiworld, player) \ | ||||||
|                 or get_option_value(world, player, 'required_tactics') > 0 and self.has_any({'Ghost', 'Spectre'}, player) |                 or get_option_value(multiworld, player, 'required_tactics') > 0 and self.has_any({'Ghost', 'Spectre'}, player) | ||||||
| 
 | 
 | ||||||
|     def _sc2wol_defense_rating(self, world: MultiWorld, player: int, zerg_enemy: bool, air_enemy: bool = True) -> bool: |     def _sc2wol_defense_rating(self, multiworld: MultiWorld, player: int, zerg_enemy: bool, air_enemy: bool = True) -> bool: | ||||||
|         defense_score = sum((defense_ratings[item] for item in defense_ratings if self.has(item, player))) |         defense_score = sum((defense_ratings[item] for item in defense_ratings if self.has(item, player))) | ||||||
|         if self.has_any({'Marine', 'Marauder'}, player) and self.has('Bunker', player): |         if self.has_any({'Marine', 'Marauder'}, player) and self.has('Bunker', player): | ||||||
|             defense_score += 3 |             defense_score += 3 | ||||||
|  | @ -35,62 +35,63 @@ class SC2WoLLogic(LogicMixin): | ||||||
|         if not air_enemy and self.has('Missile Turret', player): |         if not air_enemy and self.has('Missile Turret', player): | ||||||
|             defense_score -= defense_ratings['Missile Turret'] |             defense_score -= defense_ratings['Missile Turret'] | ||||||
|         # Advanced Tactics bumps defense rating requirements down by 2 |         # Advanced Tactics bumps defense rating requirements down by 2 | ||||||
|         if get_option_value(world, player, 'required_tactics') > 0: |         if get_option_value(multiworld, player, 'required_tactics') > 0: | ||||||
|             defense_score += 2 |             defense_score += 2 | ||||||
|         return defense_score |         return defense_score | ||||||
| 
 | 
 | ||||||
|     def _sc2wol_has_competent_comp(self, world: MultiWorld, player: int) -> bool: |     def _sc2wol_has_competent_comp(self, multiworld: MultiWorld, player: int) -> bool: | ||||||
|         return (self.has('Marine', player) or self.has('Marauder', player) and |         return (self.has('Marine', player) or self.has('Marauder', player) and | ||||||
|                 self._sc2wol_has_competent_anti_air(world, player)) and self.has_any({'Medivac', 'Medic'}, player) or \ |                 self._sc2wol_has_competent_anti_air(multiworld, player)) and self.has_any({'Medivac', 'Medic'}, player) or \ | ||||||
|                self.has('Thor', player) or self.has("Banshee", player) and self._sc2wol_has_competent_anti_air(world, player) or \ |                self.has('Thor', player) or self.has("Banshee", player) and self._sc2wol_has_competent_anti_air(multiworld, player) or \ | ||||||
|                self.has('Battlecruiser', player) and self._sc2wol_has_common_unit(world, player) or \ |                self.has('Battlecruiser', player) and self._sc2wol_has_common_unit(multiworld, player) or \ | ||||||
|                self.has('Siege Tank', player) and self._sc2wol_has_competent_anti_air(world, player) |                self.has('Siege Tank', player) and self._sc2wol_has_competent_anti_air(multiworld, player) | ||||||
| 
 | 
 | ||||||
|     def _sc2wol_has_train_killers(self, world: MultiWorld, player: int) -> bool: |     def _sc2wol_has_train_killers(self, multiworld: MultiWorld, player: int) -> bool: | ||||||
|         return (self.has_any({'Siege Tank', 'Diamondback', 'Marauder'}, player) or get_option_value(world, player, 'required_tactics') > 0 |         return (self.has_any({'Siege Tank', 'Diamondback', 'Marauder'}, player) or get_option_value(multiworld, player, 'required_tactics') > 0 | ||||||
|                 and self.has_all({'Reaper', "G-4 Clusterbomb"}, player) or self.has_all({'Spectre', 'Psionic Lash'}, player)) |                 and self.has_all({'Reaper', "G-4 Clusterbomb"}, player) or self.has_all({'Spectre', 'Psionic Lash'}, player)) | ||||||
| 
 | 
 | ||||||
|     def _sc2wol_able_to_rescue(self, world: MultiWorld, player: int) -> bool: |     def _sc2wol_able_to_rescue(self, multiworld: MultiWorld, player: int) -> bool: | ||||||
|         return self.has_any({'Medivac', 'Hercules', 'Raven', 'Viking'}, player) or get_option_value(world, player, 'required_tactics') > 0 |         return self.has_any({'Medivac', 'Hercules', 'Raven', 'Viking'}, player) or get_option_value(multiworld, player, 'required_tactics') > 0 | ||||||
| 
 | 
 | ||||||
|     def _sc2wol_has_protoss_common_units(self, world: MultiWorld, player: int) -> bool: |     def _sc2wol_has_protoss_common_units(self, multiworld: MultiWorld, player: int) -> bool: | ||||||
|         return self.has_any({'Zealot', 'Immortal', 'Stalker', 'Dark Templar'}, player) \ |         return self.has_any({'Zealot', 'Immortal', 'Stalker', 'Dark Templar'}, player) \ | ||||||
|                 or get_option_value(world, player, 'required_tactics') > 0 and self.has_any({'High Templar', 'Dark Templar'}, player) |                 or get_option_value(multiworld, player, 'required_tactics') > 0 and self.has_any({'High Templar', 'Dark Templar'}, player) | ||||||
| 
 | 
 | ||||||
|     def _sc2wol_has_protoss_medium_units(self, world: MultiWorld, player: int) -> bool: |     def _sc2wol_has_protoss_medium_units(self, multiworld: MultiWorld, player: int) -> bool: | ||||||
|         return self._sc2wol_has_protoss_common_units(world, player) and \ |         return self._sc2wol_has_protoss_common_units(multiworld, player) and \ | ||||||
|                self.has_any({'Stalker', 'Void Ray', 'Phoenix', 'Carrier'}, player) \ |                self.has_any({'Stalker', 'Void Ray', 'Phoenix', 'Carrier'}, player) \ | ||||||
|                or get_option_value(world, player, 'required_tactics') > 0 and self.has_any({'High Templar', 'Dark Templar'}, player) |                or get_option_value(multiworld, player, 'required_tactics') > 0 and self.has_any({'High Templar', 'Dark Templar'}, player) | ||||||
| 
 | 
 | ||||||
|     def _sc2wol_beats_protoss_deathball(self, world: MultiWorld, player: int) -> bool: |     def _sc2wol_beats_protoss_deathball(self, multiworld: MultiWorld, player: int) -> bool: | ||||||
|         return self.has_any({'Banshee', 'Battlecruiser'}, player) and self._sc2wol_has_competent_anti_air or \ |         return self.has_any({'Banshee', 'Battlecruiser'}, player) and self._sc2wol_has_competent_anti_air(multiworld, player) or \ | ||||||
|                self._sc2wol_has_competent_comp(world, player) and self._sc2wol_has_air_anti_air(world, player) |                self._sc2wol_has_competent_comp(multiworld, player) and self._sc2wol_has_air_anti_air(multiworld, player) | ||||||
| 
 | 
 | ||||||
|     def _sc2wol_has_mm_upgrade(self, world: MultiWorld, player: int) -> bool: |     def _sc2wol_has_mm_upgrade(self, multiworld: MultiWorld, player: int) -> bool: | ||||||
|         return self.has_any({"Combat Shield (Marine)", "Stabilizer Medpacks (Medic)"}, player) |         return self.has_any({"Combat Shield (Marine)", "Stabilizer Medpacks (Medic)"}, player) | ||||||
| 
 | 
 | ||||||
|     def _sc2wol_survives_rip_field(self, world: MultiWorld, player: int) -> bool: |     def _sc2wol_survives_rip_field(self, multiworld: MultiWorld, player: int) -> bool: | ||||||
|         return self.has("Battlecruiser", player) or \ |         return self.has("Battlecruiser", player) or \ | ||||||
|            self._sc2wol_has_air(world, player) and \ |            self._sc2wol_has_air(multiworld, player) and \ | ||||||
|            self._sc2wol_has_competent_anti_air(world, player) and \ |            self._sc2wol_has_competent_anti_air(multiworld, player) and \ | ||||||
|            self.has("Science Vessel", player) |            self.has("Science Vessel", player) | ||||||
| 
 | 
 | ||||||
|     def _sc2wol_has_nukes(self, world: MultiWorld, player: int) -> bool: |     def _sc2wol_has_nukes(self, multiworld: MultiWorld, player: int) -> bool: | ||||||
|         return get_option_value(world, player, 'required_tactics') > 0 and self.has_any({'Ghost', 'Spectre'}, player) |         return get_option_value(multiworld, player, 'required_tactics') > 0 and self.has_any({'Ghost', 'Spectre'}, player) | ||||||
| 
 | 
 | ||||||
|     def _sc2wol_final_mission_requirements(self, world: MultiWorld, player: int): |     def _sc2wol_final_mission_requirements(self, multiworld: MultiWorld, player: int): | ||||||
|         defense_rating = self._sc2wol_defense_rating(world, player, True) |         beats_kerrigan = self.has_any({'Marine', 'Banshee', 'Ghost'}, player) or get_option_value(multiworld, player, 'required_tactics') > 0 | ||||||
|         beats_kerrigan = self.has_any({'Marine', 'Banshee', 'Ghost'}, player) or get_option_value(world, player, 'required_tactics') > 0 |         if get_option_value(multiworld, player, 'all_in_map') == 0: | ||||||
|         if get_option_value(world, player, 'all_in_map') == 0: |  | ||||||
|             # Ground |             # Ground | ||||||
|  |             defense_rating = self._sc2wol_defense_rating(multiworld, player, True, False) | ||||||
|             if self.has_any({'Battlecruiser', 'Banshee'}, player): |             if self.has_any({'Battlecruiser', 'Banshee'}, player): | ||||||
|                 defense_rating += 3 |                 defense_rating += 3 | ||||||
|             return defense_rating >= 12 and beats_kerrigan |             return defense_rating >= 12 and beats_kerrigan | ||||||
|         else: |         else: | ||||||
|             # Air |             # Air | ||||||
|  |             defense_rating = self._sc2wol_defense_rating(multiworld, player, True, True) | ||||||
|             return defense_rating >= 8 and beats_kerrigan \ |             return defense_rating >= 8 and beats_kerrigan \ | ||||||
|                 and self.has_any({'Viking', 'Battlecruiser'}, player) \ |                 and self.has_any({'Viking', 'Battlecruiser'}, player) \ | ||||||
|                 and self.has_any({'Hive Mind Emulator', 'Psi Disruptor', 'Missile Turret'}, player) |                 and self.has_any({'Hive Mind Emulator', 'Psi Disruptor', 'Missile Turret'}, player) | ||||||
| 
 | 
 | ||||||
|     def _sc2wol_cleared_missions(self, world: MultiWorld, player: int, mission_count: int) -> bool: |     def _sc2wol_cleared_missions(self, multiworld: MultiWorld, player: int, mission_count: int) -> bool: | ||||||
|         return self.has_group("Missions", player, mission_count) |         return self.has_group("Missions", player, mission_count) | ||||||
|  |  | ||||||
|  | @ -197,12 +197,12 @@ advanced_starting_mission_locations = { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def get_starting_mission_locations(world: MultiWorld, player: int) -> Set[str]: | def get_starting_mission_locations(multiworld: MultiWorld, player: int) -> Set[str]: | ||||||
|     if get_option_value(world, player, 'shuffle_no_build') or get_option_value(world, player, 'mission_order') < 2: |     if get_option_value(multiworld, player, 'shuffle_no_build') or get_option_value(multiworld, player, 'mission_order') < 2: | ||||||
|         # Always start with a no-build mission unless explicitly relegating them |         # Always start with a no-build mission unless explicitly relegating them | ||||||
|         # Vanilla and Vanilla Shuffled always start with a no-build even when relegated |         # Vanilla and Vanilla Shuffled always start with a no-build even when relegated | ||||||
|         return no_build_starting_mission_locations |         return no_build_starting_mission_locations | ||||||
|     elif get_option_value(world, player, 'required_tactics') > 0: |     elif get_option_value(multiworld, player, 'required_tactics') > 0: | ||||||
|         # Advanced Tactics/No Logic add more starting missions to the pool |         # Advanced Tactics/No Logic add more starting missions to the pool | ||||||
|         return {**build_starting_mission_locations, **advanced_starting_mission_locations} |         return {**build_starting_mission_locations, **advanced_starting_mission_locations} | ||||||
|     else: |     else: | ||||||
|  |  | ||||||
|  | @ -130,8 +130,8 @@ sc2wol_options: Dict[str, Option] = { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def get_option_value(world: MultiWorld, player: int, name: str) -> int: | def get_option_value(multiworld: MultiWorld, player: int, name: str) -> int: | ||||||
|     option = getattr(world, name, None) |     option = getattr(multiworld, name, None) | ||||||
| 
 | 
 | ||||||
|     if option is None: |     if option is None: | ||||||
|         return 0 |         return 0 | ||||||
|  | @ -139,8 +139,8 @@ def get_option_value(world: MultiWorld, player: int, name: str) -> int: | ||||||
|     return int(option[player].value) |     return int(option[player].value) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def get_option_set_value(world: MultiWorld, player: int, name: str) -> set: | def get_option_set_value(multiworld: MultiWorld, player: int, name: str) -> set: | ||||||
|     option = getattr(world, name, None) |     option = getattr(multiworld, name, None) | ||||||
| 
 | 
 | ||||||
|     if option is None: |     if option is None: | ||||||
|         return set() |         return set() | ||||||
|  |  | ||||||
|  | @ -21,14 +21,14 @@ STARPORT_UNITS = {"Medivac", "Wraith", "Viking", "Banshee", "Battlecruiser", "He | ||||||
| PROTOSS_REGIONS = {"A Sinister Turn", "Echoes of the Future", "In Utter Darkness"} | PROTOSS_REGIONS = {"A Sinister Turn", "Echoes of the Future", "In Utter Darkness"} | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def filter_missions(world: MultiWorld, player: int) -> Dict[str, List[str]]: | def filter_missions(multiworld: MultiWorld, player: int) -> Dict[str, List[str]]: | ||||||
|     """ |     """ | ||||||
|     Returns a semi-randomly pruned tuple of no-build, easy, medium, and hard mission sets |     Returns a semi-randomly pruned tuple of no-build, easy, medium, and hard mission sets | ||||||
|     """ |     """ | ||||||
| 
 | 
 | ||||||
|     mission_order_type = get_option_value(world, player, "mission_order") |     mission_order_type = get_option_value(multiworld, player, "mission_order") | ||||||
|     shuffle_protoss = get_option_value(world, player, "shuffle_protoss") |     shuffle_protoss = get_option_value(multiworld, player, "shuffle_protoss") | ||||||
|     excluded_missions = set(get_option_set_value(world, player, "excluded_missions")) |     excluded_missions = set(get_option_set_value(multiworld, player, "excluded_missions")) | ||||||
|     invalid_mission_names = excluded_missions.difference(vanilla_mission_req_table.keys()) |     invalid_mission_names = excluded_missions.difference(vanilla_mission_req_table.keys()) | ||||||
|     if invalid_mission_names: |     if invalid_mission_names: | ||||||
|         raise Exception("Error in locked_missions - the following are not valid mission names: " + ", ".join(invalid_mission_names)) |         raise Exception("Error in locked_missions - the following are not valid mission names: " + ", ".join(invalid_mission_names)) | ||||||
|  | @ -54,17 +54,17 @@ def filter_missions(world: MultiWorld, player: int) -> Dict[str, List[str]]: | ||||||
|         excluded_missions = excluded_missions.union(PROTOSS_REGIONS) |         excluded_missions = excluded_missions.union(PROTOSS_REGIONS) | ||||||
|     # Replacing All-In on low mission counts |     # Replacing All-In on low mission counts | ||||||
|     if mission_count < 14: |     if mission_count < 14: | ||||||
|         final_mission = world.random.choice([mission for mission in alt_final_mission_locations.keys() if mission not in excluded_missions]) |         final_mission = multiworld.random.choice([mission for mission in alt_final_mission_locations.keys() if mission not in excluded_missions]) | ||||||
|         excluded_missions.add(final_mission) |         excluded_missions.add(final_mission) | ||||||
|     else: |     else: | ||||||
|         final_mission = 'All-In' |         final_mission = 'All-In' | ||||||
|     # Yaml settings determine which missions can be placed in the first slot |     # Yaml settings determine which missions can be placed in the first slot | ||||||
|     mission_pools[0] = [mission for mission in get_starting_mission_locations(world, player).keys() if mission not in excluded_missions] |     mission_pools[0] = [mission for mission in get_starting_mission_locations(multiworld, player).keys() if mission not in excluded_missions] | ||||||
|     # Removing the new no-build missions from their original sets |     # Removing the new no-build missions from their original sets | ||||||
|     for i in range(1, len(mission_pools)): |     for i in range(1, len(mission_pools)): | ||||||
|         mission_pools[i] = [mission for mission in mission_pools[i] if mission not in excluded_missions.union(mission_pools[0])] |         mission_pools[i] = [mission for mission in mission_pools[i] if mission not in excluded_missions.union(mission_pools[0])] | ||||||
|     # If the first mission is a build mission, there may not be enough locations to reach Outbreak as a second mission |     # If the first mission is a build mission, there may not be enough locations to reach Outbreak as a second mission | ||||||
|     if not get_option_value(world, player, 'shuffle_no_build'): |     if not get_option_value(multiworld, player, 'shuffle_no_build'): | ||||||
|         # Swapping Outbreak and The Great Train Robbery |         # Swapping Outbreak and The Great Train Robbery | ||||||
|         if "Outbreak" in mission_pools[1]: |         if "Outbreak" in mission_pools[1]: | ||||||
|             mission_pools[1].remove("Outbreak") |             mission_pools[1].remove("Outbreak") | ||||||
|  | @ -87,7 +87,7 @@ def filter_missions(world: MultiWorld, player: int) -> Dict[str, List[str]]: | ||||||
|             if all(len(mission_pool) <= 1 for mission_pool in mission_pools): |             if all(len(mission_pool) <= 1 for mission_pool in mission_pools): | ||||||
|                 raise Exception("Not enough missions available to fill the campaign on current settings.  Please exclude fewer missions.") |                 raise Exception("Not enough missions available to fill the campaign on current settings.  Please exclude fewer missions.") | ||||||
|         else: |         else: | ||||||
|             mission_pool.remove(world.random.choice(mission_pool)) |             mission_pool.remove(multiworld.random.choice(mission_pool)) | ||||||
|             current_count -= 1 |             current_count -= 1 | ||||||
|         set_cycle += 1 |         set_cycle += 1 | ||||||
| 
 | 
 | ||||||
|  | @ -134,7 +134,7 @@ class ValidInventory: | ||||||
|         } |         } | ||||||
|         requirements = mission_requirements |         requirements = mission_requirements | ||||||
|         cascade_keys = self.cascade_removal_map.keys() |         cascade_keys = self.cascade_removal_map.keys() | ||||||
|         units_always_have_upgrades = get_option_value(self.world, self.player, "units_always_have_upgrades") |         units_always_have_upgrades = get_option_value(self.multiworld, self.player, "units_always_have_upgrades") | ||||||
|         if self.min_units_per_structure > 0: |         if self.min_units_per_structure > 0: | ||||||
|             requirements.append(lambda state: state.has_units_per_structure()) |             requirements.append(lambda state: state.has_units_per_structure()) | ||||||
| 
 | 
 | ||||||
|  | @ -155,7 +155,7 @@ class ValidInventory: | ||||||
|             if len(inventory) == 0: |             if len(inventory) == 0: | ||||||
|                 raise Exception("Reduced item pool generation failed - not enough locations available to place items.") |                 raise Exception("Reduced item pool generation failed - not enough locations available to place items.") | ||||||
|             # Select random item from removable items |             # Select random item from removable items | ||||||
|             item = self.world.random.choice(inventory) |             item = self.multiworld.random.choice(inventory) | ||||||
|             # Cascade removals to associated items |             # Cascade removals to associated items | ||||||
|             if item in cascade_keys: |             if item in cascade_keys: | ||||||
|                 items_to_remove = self.cascade_removal_map[item] |                 items_to_remove = self.cascade_removal_map[item] | ||||||
|  | @ -206,10 +206,10 @@ class ValidInventory: | ||||||
|         self._sc2wol_has_mm_upgrade = lambda world, player: SC2WoLLogic._sc2wol_has_mm_upgrade(self, world, player) |         self._sc2wol_has_mm_upgrade = lambda world, player: SC2WoLLogic._sc2wol_has_mm_upgrade(self, world, player) | ||||||
|         self._sc2wol_final_mission_requirements = lambda world, player: SC2WoLLogic._sc2wol_final_mission_requirements(self, world, player) |         self._sc2wol_final_mission_requirements = lambda world, player: SC2WoLLogic._sc2wol_final_mission_requirements(self, world, player) | ||||||
| 
 | 
 | ||||||
|     def __init__(self, world: MultiWorld, player: int, |     def __init__(self, multiworld: MultiWorld, player: int, | ||||||
|                  item_pool: List[Item], existing_items: List[Item], locked_items: List[Item], |                  item_pool: List[Item], existing_items: List[Item], locked_items: List[Item], | ||||||
|                  has_protoss: bool): |                  has_protoss: bool): | ||||||
|         self.world = world |         self.multiworld = multiworld | ||||||
|         self.player = player |         self.player = player | ||||||
|         self.logical_inventory = set() |         self.logical_inventory = set() | ||||||
|         self.locked_items = locked_items[:] |         self.locked_items = locked_items[:] | ||||||
|  | @ -219,7 +219,7 @@ class ValidInventory: | ||||||
|         self.item_pool = [] |         self.item_pool = [] | ||||||
|         item_quantities: dict[str, int] = dict() |         item_quantities: dict[str, int] = dict() | ||||||
|         # Inventory restrictiveness based on number of missions with checks |         # Inventory restrictiveness based on number of missions with checks | ||||||
|         mission_order_type = get_option_value(self.world, self.player, "mission_order") |         mission_order_type = get_option_value(self.multiworld, self.player, "mission_order") | ||||||
|         mission_count = len(mission_orders[mission_order_type]) - 1 |         mission_count = len(mission_orders[mission_order_type]) - 1 | ||||||
|         self.min_units_per_structure = int(mission_count / 7) |         self.min_units_per_structure = int(mission_count / 7) | ||||||
|         min_upgrades = 1 if mission_count < 10 else 2 |         min_upgrades = 1 if mission_count < 10 else 2 | ||||||
|  | @ -244,12 +244,12 @@ class ValidInventory: | ||||||
|                 upgrades = get_item_upgrades(self.item_pool, item) |                 upgrades = get_item_upgrades(self.item_pool, item) | ||||||
|                 associated_items = [*upgrades, item] |                 associated_items = [*upgrades, item] | ||||||
|                 self.cascade_removal_map[item] = associated_items |                 self.cascade_removal_map[item] = associated_items | ||||||
|                 if get_option_value(world, player, "units_always_have_upgrades"): |                 if get_option_value(multiworld, player, "units_always_have_upgrades"): | ||||||
|                     for upgrade in upgrades: |                     for upgrade in upgrades: | ||||||
|                         self.cascade_removal_map[upgrade] = associated_items |                         self.cascade_removal_map[upgrade] = associated_items | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def filter_items(world: MultiWorld, player: int, mission_req_table: Dict[str, MissionInfo], location_cache: List[Location], | def filter_items(multiworld: MultiWorld, player: int, mission_req_table: Dict[str, MissionInfo], location_cache: List[Location], | ||||||
|                  item_pool: List[Item], existing_items: List[Item], locked_items: List[Item]) -> List[Item]: |                  item_pool: List[Item], existing_items: List[Item], locked_items: List[Item]) -> List[Item]: | ||||||
|     """ |     """ | ||||||
|     Returns a semi-randomly pruned set of items based on number of available locations. |     Returns a semi-randomly pruned set of items based on number of available locations. | ||||||
|  | @ -259,7 +259,7 @@ def filter_items(world: MultiWorld, player: int, mission_req_table: Dict[str, Mi | ||||||
|     inventory_size = len(open_locations) |     inventory_size = len(open_locations) | ||||||
|     has_protoss = bool(PROTOSS_REGIONS.intersection(mission_req_table.keys())) |     has_protoss = bool(PROTOSS_REGIONS.intersection(mission_req_table.keys())) | ||||||
|     mission_requirements = [location.access_rule for location in location_cache] |     mission_requirements = [location.access_rule for location in location_cache] | ||||||
|     valid_inventory = ValidInventory(world, player, item_pool, existing_items, locked_items, has_protoss) |     valid_inventory = ValidInventory(multiworld, player, item_pool, existing_items, locked_items, has_protoss) | ||||||
| 
 | 
 | ||||||
|     valid_items = valid_inventory.generate_reduced_inventory(inventory_size, mission_requirements) |     valid_items = valid_inventory.generate_reduced_inventory(inventory_size, mission_requirements) | ||||||
|     return valid_items |     return valid_items | ||||||
|  |  | ||||||
|  | @ -4,23 +4,22 @@ from .Locations import LocationData | ||||||
| from .Options import get_option_value | from .Options import get_option_value | ||||||
| from .MissionTables import MissionInfo, mission_orders, vanilla_mission_req_table, alt_final_mission_locations | from .MissionTables import MissionInfo, mission_orders, vanilla_mission_req_table, alt_final_mission_locations | ||||||
| from .PoolFilter import filter_missions | from .PoolFilter import filter_missions | ||||||
| import random |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def create_regions(world: MultiWorld, player: int, locations: Tuple[LocationData, ...], location_cache: List[Location])\ | def create_regions(multiworld: MultiWorld, player: int, locations: Tuple[LocationData, ...], location_cache: List[Location])\ | ||||||
|         -> Tuple[Dict[str, MissionInfo], int, str]: |         -> Tuple[Dict[str, MissionInfo], int, str]: | ||||||
|     locations_per_region = get_locations_per_region(locations) |     locations_per_region = get_locations_per_region(locations) | ||||||
| 
 | 
 | ||||||
|     mission_order_type = get_option_value(world, player, "mission_order") |     mission_order_type = get_option_value(multiworld, player, "mission_order") | ||||||
|     mission_order = mission_orders[mission_order_type] |     mission_order = mission_orders[mission_order_type] | ||||||
| 
 | 
 | ||||||
|     mission_pools = filter_missions(world, player) |     mission_pools = filter_missions(multiworld, player) | ||||||
|     final_mission = mission_pools['all_in'][0] |     final_mission = mission_pools['all_in'][0] | ||||||
| 
 | 
 | ||||||
|     used_regions = [mission for mission_pool in mission_pools.values() for mission in mission_pool] |     used_regions = [mission for mission_pool in mission_pools.values() for mission in mission_pool] | ||||||
|     regions = [create_region(world, player, locations_per_region, location_cache, "Menu")] |     regions = [create_region(multiworld, player, locations_per_region, location_cache, "Menu")] | ||||||
|     for region_name in used_regions: |     for region_name in used_regions: | ||||||
|         regions.append(create_region(world, player, locations_per_region, location_cache, region_name)) |         regions.append(create_region(multiworld, player, locations_per_region, location_cache, region_name)) | ||||||
|     # Changing the completion condition for alternate final missions into an event |     # Changing the completion condition for alternate final missions into an event | ||||||
|     if final_mission != 'All-In': |     if final_mission != 'All-In': | ||||||
|         final_location = alt_final_mission_locations[final_mission] |         final_location = alt_final_mission_locations[final_mission] | ||||||
|  | @ -38,76 +37,76 @@ def create_regions(world: MultiWorld, player: int, locations: Tuple[LocationData | ||||||
|         if mission_order_type in (0, 1): |         if mission_order_type in (0, 1): | ||||||
|             throwIfAnyLocationIsNotAssignedToARegion(regions, locations_per_region.keys()) |             throwIfAnyLocationIsNotAssignedToARegion(regions, locations_per_region.keys()) | ||||||
| 
 | 
 | ||||||
|     world.regions += regions |     multiworld.regions += regions | ||||||
| 
 | 
 | ||||||
|     names: Dict[str, int] = {} |     names: Dict[str, int] = {} | ||||||
| 
 | 
 | ||||||
|     if mission_order_type == 0: |     if mission_order_type == 0: | ||||||
|         connect(world, player, names, 'Menu', 'Liberation Day'), |         connect(multiworld, player, names, 'Menu', 'Liberation Day'), | ||||||
|         connect(world, player, names, 'Liberation Day', 'The Outlaws', |         connect(multiworld, player, names, 'Liberation Day', 'The Outlaws', | ||||||
|                 lambda state: state.has("Beat Liberation Day", player)), |                 lambda state: state.has("Beat Liberation Day", player)), | ||||||
|         connect(world, player, names, 'The Outlaws', 'Zero Hour', |         connect(multiworld, player, names, 'The Outlaws', 'Zero Hour', | ||||||
|                 lambda state: state.has("Beat The Outlaws", player)), |                 lambda state: state.has("Beat The Outlaws", player)), | ||||||
|         connect(world, player, names, 'Zero Hour', 'Evacuation', |         connect(multiworld, player, names, 'Zero Hour', 'Evacuation', | ||||||
|                 lambda state: state.has("Beat Zero Hour", player)), |                 lambda state: state.has("Beat Zero Hour", player)), | ||||||
|         connect(world, player, names, 'Evacuation', 'Outbreak', |         connect(multiworld, player, names, 'Evacuation', 'Outbreak', | ||||||
|                 lambda state: state.has("Beat Evacuation", player)), |                 lambda state: state.has("Beat Evacuation", player)), | ||||||
|         connect(world, player, names, "Outbreak", "Safe Haven", |         connect(multiworld, player, names, "Outbreak", "Safe Haven", | ||||||
|                 lambda state: state._sc2wol_cleared_missions(world, player, 7) and |                 lambda state: state._sc2wol_cleared_missions(multiworld, player, 7) and | ||||||
|                               state.has("Beat Outbreak", player)), |                               state.has("Beat Outbreak", player)), | ||||||
|         connect(world, player, names, "Outbreak", "Haven's Fall", |         connect(multiworld, player, names, "Outbreak", "Haven's Fall", | ||||||
|                 lambda state: state._sc2wol_cleared_missions(world, player, 7) and |                 lambda state: state._sc2wol_cleared_missions(multiworld, player, 7) and | ||||||
|                               state.has("Beat Outbreak", player)), |                               state.has("Beat Outbreak", player)), | ||||||
|         connect(world, player, names, 'Zero Hour', 'Smash and Grab', |         connect(multiworld, player, names, 'Zero Hour', 'Smash and Grab', | ||||||
|                 lambda state: state.has("Beat Zero Hour", player)), |                 lambda state: state.has("Beat Zero Hour", player)), | ||||||
|         connect(world, player, names, 'Smash and Grab', 'The Dig', |         connect(multiworld, player, names, 'Smash and Grab', 'The Dig', | ||||||
|                 lambda state: state._sc2wol_cleared_missions(world, player, 8) and |                 lambda state: state._sc2wol_cleared_missions(multiworld, player, 8) and | ||||||
|                               state.has("Beat Smash and Grab", player)), |                               state.has("Beat Smash and Grab", player)), | ||||||
|         connect(world, player, names, 'The Dig', 'The Moebius Factor', |         connect(multiworld, player, names, 'The Dig', 'The Moebius Factor', | ||||||
|                 lambda state: state._sc2wol_cleared_missions(world, player, 11) and |                 lambda state: state._sc2wol_cleared_missions(multiworld, player, 11) and | ||||||
|                               state.has("Beat The Dig", player)), |                               state.has("Beat The Dig", player)), | ||||||
|         connect(world, player, names, 'The Moebius Factor', 'Supernova', |         connect(multiworld, player, names, 'The Moebius Factor', 'Supernova', | ||||||
|                 lambda state: state._sc2wol_cleared_missions(world, player, 14) and |                 lambda state: state._sc2wol_cleared_missions(multiworld, player, 14) and | ||||||
|                               state.has("Beat The Moebius Factor", player)), |                               state.has("Beat The Moebius Factor", player)), | ||||||
|         connect(world, player, names, 'Supernova', 'Maw of the Void', |         connect(multiworld, player, names, 'Supernova', 'Maw of the Void', | ||||||
|                 lambda state: state.has("Beat Supernova", player)), |                 lambda state: state.has("Beat Supernova", player)), | ||||||
|         connect(world, player, names, 'Zero Hour', "Devil's Playground", |         connect(multiworld, player, names, 'Zero Hour', "Devil's Playground", | ||||||
|                 lambda state: state._sc2wol_cleared_missions(world, player, 4) and |                 lambda state: state._sc2wol_cleared_missions(multiworld, player, 4) and | ||||||
|                               state.has("Beat Zero Hour", player)), |                               state.has("Beat Zero Hour", player)), | ||||||
|         connect(world, player, names, "Devil's Playground", 'Welcome to the Jungle', |         connect(multiworld, player, names, "Devil's Playground", 'Welcome to the Jungle', | ||||||
|                 lambda state: state.has("Beat Devil's Playground", player)), |                 lambda state: state.has("Beat Devil's Playground", player)), | ||||||
|         connect(world, player, names, "Welcome to the Jungle", 'Breakout', |         connect(multiworld, player, names, "Welcome to the Jungle", 'Breakout', | ||||||
|                 lambda state: state._sc2wol_cleared_missions(world, player, 8) and |                 lambda state: state._sc2wol_cleared_missions(multiworld, player, 8) and | ||||||
|                               state.has("Beat Welcome to the Jungle", player)), |                               state.has("Beat Welcome to the Jungle", player)), | ||||||
|         connect(world, player, names, "Welcome to the Jungle", 'Ghost of a Chance', |         connect(multiworld, player, names, "Welcome to the Jungle", 'Ghost of a Chance', | ||||||
|                 lambda state: state._sc2wol_cleared_missions(world, player, 8) and |                 lambda state: state._sc2wol_cleared_missions(multiworld, player, 8) and | ||||||
|                               state.has("Beat Welcome to the Jungle", player)), |                               state.has("Beat Welcome to the Jungle", player)), | ||||||
|         connect(world, player, names, "Zero Hour", 'The Great Train Robbery', |         connect(multiworld, player, names, "Zero Hour", 'The Great Train Robbery', | ||||||
|                 lambda state: state._sc2wol_cleared_missions(world, player, 6) and |                 lambda state: state._sc2wol_cleared_missions(multiworld, player, 6) and | ||||||
|                               state.has("Beat Zero Hour", player)), |                               state.has("Beat Zero Hour", player)), | ||||||
|         connect(world, player, names, 'The Great Train Robbery', 'Cutthroat', |         connect(multiworld, player, names, 'The Great Train Robbery', 'Cutthroat', | ||||||
|                 lambda state: state.has("Beat The Great Train Robbery", player)), |                 lambda state: state.has("Beat The Great Train Robbery", player)), | ||||||
|         connect(world, player, names, 'Cutthroat', 'Engine of Destruction', |         connect(multiworld, player, names, 'Cutthroat', 'Engine of Destruction', | ||||||
|                 lambda state: state.has("Beat Cutthroat", player)), |                 lambda state: state.has("Beat Cutthroat", player)), | ||||||
|         connect(world, player, names, 'Engine of Destruction', 'Media Blitz', |         connect(multiworld, player, names, 'Engine of Destruction', 'Media Blitz', | ||||||
|                 lambda state: state.has("Beat Engine of Destruction", player)), |                 lambda state: state.has("Beat Engine of Destruction", player)), | ||||||
|         connect(world, player, names, 'Media Blitz', 'Piercing the Shroud', |         connect(multiworld, player, names, 'Media Blitz', 'Piercing the Shroud', | ||||||
|                 lambda state: state.has("Beat Media Blitz", player)), |                 lambda state: state.has("Beat Media Blitz", player)), | ||||||
|         connect(world, player, names, 'The Dig', 'Whispers of Doom', |         connect(multiworld, player, names, 'The Dig', 'Whispers of Doom', | ||||||
|                 lambda state: state.has("Beat The Dig", player)), |                 lambda state: state.has("Beat The Dig", player)), | ||||||
|         connect(world, player, names, 'Whispers of Doom', 'A Sinister Turn', |         connect(multiworld, player, names, 'Whispers of Doom', 'A Sinister Turn', | ||||||
|                 lambda state: state.has("Beat Whispers of Doom", player)), |                 lambda state: state.has("Beat Whispers of Doom", player)), | ||||||
|         connect(world, player, names, 'A Sinister Turn', 'Echoes of the Future', |         connect(multiworld, player, names, 'A Sinister Turn', 'Echoes of the Future', | ||||||
|                 lambda state: state.has("Beat A Sinister Turn", player)), |                 lambda state: state.has("Beat A Sinister Turn", player)), | ||||||
|         connect(world, player, names, 'Echoes of the Future', 'In Utter Darkness', |         connect(multiworld, player, names, 'Echoes of the Future', 'In Utter Darkness', | ||||||
|                 lambda state: state.has("Beat Echoes of the Future", player)), |                 lambda state: state.has("Beat Echoes of the Future", player)), | ||||||
|         connect(world, player, names, 'Maw of the Void', 'Gates of Hell', |         connect(multiworld, player, names, 'Maw of the Void', 'Gates of Hell', | ||||||
|                 lambda state: state.has("Beat Maw of the Void", player)), |                 lambda state: state.has("Beat Maw of the Void", player)), | ||||||
|         connect(world, player, names, 'Gates of Hell', 'Belly of the Beast', |         connect(multiworld, player, names, 'Gates of Hell', 'Belly of the Beast', | ||||||
|                 lambda state: state.has("Beat Gates of Hell", player)), |                 lambda state: state.has("Beat Gates of Hell", player)), | ||||||
|         connect(world, player, names, 'Gates of Hell', 'Shatter the Sky', |         connect(multiworld, player, names, 'Gates of Hell', 'Shatter the Sky', | ||||||
|                 lambda state: state.has("Beat Gates of Hell", player)), |                 lambda state: state.has("Beat Gates of Hell", player)), | ||||||
|         connect(world, player, names, 'Gates of Hell', 'All-In', |         connect(multiworld, player, names, 'Gates of Hell', 'All-In', | ||||||
|                 lambda state: state.has('Beat Gates of Hell', player) and ( |                 lambda state: state.has('Beat Gates of Hell', player) and ( | ||||||
|                         state.has('Beat Shatter the Sky', player) or state.has('Beat Belly of the Beast', player))) |                         state.has('Beat Shatter the Sky', player) or state.has('Beat Belly of the Beast', player))) | ||||||
| 
 | 
 | ||||||
|  | @ -122,13 +121,13 @@ def create_regions(world: MultiWorld, player: int, locations: Tuple[LocationData | ||||||
|                 missions.append(None) |                 missions.append(None) | ||||||
|             elif mission.type == "all_in": |             elif mission.type == "all_in": | ||||||
|                 missions.append(final_mission) |                 missions.append(final_mission) | ||||||
|             elif mission.relegate and not get_option_value(world, player, "shuffle_no_build"): |             elif mission.relegate and not get_option_value(multiworld, player, "shuffle_no_build"): | ||||||
|                 missions.append("no_build") |                 missions.append("no_build") | ||||||
|             else: |             else: | ||||||
|                 missions.append(mission.type) |                 missions.append(mission.type) | ||||||
| 
 | 
 | ||||||
|         # Place Protoss Missions if we are not using ShuffleProtoss and are in Vanilla Shuffled |         # Place Protoss Missions if we are not using ShuffleProtoss and are in Vanilla Shuffled | ||||||
|         if get_option_value(world, player, "shuffle_protoss") == 0 and mission_order_type == 1: |         if get_option_value(multiworld, player, "shuffle_protoss") == 0 and mission_order_type == 1: | ||||||
|             missions[22] = "A Sinister Turn" |             missions[22] = "A Sinister Turn" | ||||||
|             mission_pools['medium'].remove("A Sinister Turn") |             mission_pools['medium'].remove("A Sinister Turn") | ||||||
|             missions[23] = "Echoes of the Future" |             missions[23] = "Echoes of the Future" | ||||||
|  | @ -157,28 +156,28 @@ def create_regions(world: MultiWorld, player: int, locations: Tuple[LocationData | ||||||
|         # Add no_build missions to the pool and fill in no_build slots |         # Add no_build missions to the pool and fill in no_build slots | ||||||
|         missions_to_add = mission_pools['no_build'] |         missions_to_add = mission_pools['no_build'] | ||||||
|         for slot in no_build_slots: |         for slot in no_build_slots: | ||||||
|             filler = world.random.randint(0, len(missions_to_add)-1) |             filler = multiworld.random.randint(0, len(missions_to_add) - 1) | ||||||
| 
 | 
 | ||||||
|             missions[slot] = missions_to_add.pop(filler) |             missions[slot] = missions_to_add.pop(filler) | ||||||
| 
 | 
 | ||||||
|         # Add easy missions into pool and fill in easy slots |         # Add easy missions into pool and fill in easy slots | ||||||
|         missions_to_add = missions_to_add + mission_pools['easy'] |         missions_to_add = missions_to_add + mission_pools['easy'] | ||||||
|         for slot in easy_slots: |         for slot in easy_slots: | ||||||
|             filler = world.random.randint(0, len(missions_to_add) - 1) |             filler = multiworld.random.randint(0, len(missions_to_add) - 1) | ||||||
| 
 | 
 | ||||||
|             missions[slot] = missions_to_add.pop(filler) |             missions[slot] = missions_to_add.pop(filler) | ||||||
| 
 | 
 | ||||||
|         # Add medium missions into pool and fill in medium slots |         # Add medium missions into pool and fill in medium slots | ||||||
|         missions_to_add = missions_to_add + mission_pools['medium'] |         missions_to_add = missions_to_add + mission_pools['medium'] | ||||||
|         for slot in medium_slots: |         for slot in medium_slots: | ||||||
|             filler = world.random.randint(0, len(missions_to_add) - 1) |             filler = multiworld.random.randint(0, len(missions_to_add) - 1) | ||||||
| 
 | 
 | ||||||
|             missions[slot] = missions_to_add.pop(filler) |             missions[slot] = missions_to_add.pop(filler) | ||||||
| 
 | 
 | ||||||
|         # Add hard missions into pool and fill in hard slots |         # Add hard missions into pool and fill in hard slots | ||||||
|         missions_to_add = missions_to_add + mission_pools['hard'] |         missions_to_add = missions_to_add + mission_pools['hard'] | ||||||
|         for slot in hard_slots: |         for slot in hard_slots: | ||||||
|             filler = world.random.randint(0, len(missions_to_add) - 1) |             filler = multiworld.random.randint(0, len(missions_to_add) - 1) | ||||||
| 
 | 
 | ||||||
|             missions[slot] = missions_to_add.pop(filler) |             missions[slot] = missions_to_add.pop(filler) | ||||||
| 
 | 
 | ||||||
|  | @ -189,11 +188,11 @@ def create_regions(world: MultiWorld, player: int, locations: Tuple[LocationData | ||||||
|             connections = [] |             connections = [] | ||||||
|             for connection in mission_order[i].connect_to: |             for connection in mission_order[i].connect_to: | ||||||
|                 if connection == -1: |                 if connection == -1: | ||||||
|                     connect(world, player, names, "Menu", missions[i]) |                     connect(multiworld, player, names, "Menu", missions[i]) | ||||||
|                 else: |                 else: | ||||||
|                     connect(world, player, names, missions[connection], missions[i], |                     connect(multiworld, player, names, missions[connection], missions[i], | ||||||
|                             (lambda name, missions_req: (lambda state: state.has(f"Beat {name}", player) and |                             (lambda name, missions_req: (lambda state: state.has(f"Beat {name}", player) and | ||||||
|                                                                        state._sc2wol_cleared_missions(world, player, |                                                                        state._sc2wol_cleared_missions(multiworld, player, | ||||||
|                                                                                                       missions_req))) |                                                                                                       missions_req))) | ||||||
|                             (missions[connection], mission_order[i].number)) |                             (missions[connection], mission_order[i].number)) | ||||||
|                     connections.append(connection + 1) |                     connections.append(connection + 1) | ||||||
|  | @ -233,10 +232,10 @@ def create_location(player: int, location_data: LocationData, region: Region, | ||||||
|     return location |     return location | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def create_region(world: MultiWorld, player: int, locations_per_region: Dict[str, List[LocationData]], | def create_region(multiworld: MultiWorld, player: int, locations_per_region: Dict[str, List[LocationData]], | ||||||
|                   location_cache: List[Location], name: str) -> Region: |                   location_cache: List[Location], name: str) -> Region: | ||||||
|     region = Region(name, RegionType.Generic, name, player) |     region = Region(name, RegionType.Generic, name, player) | ||||||
|     region.multiworld = world |     region.multiworld = multiworld | ||||||
| 
 | 
 | ||||||
|     if name in locations_per_region: |     if name in locations_per_region: | ||||||
|         for location_data in locations_per_region[name]: |         for location_data in locations_per_region[name]: | ||||||
|  |  | ||||||
|  | @ -48,8 +48,8 @@ class SC2WoLWorld(World): | ||||||
|     victory_item: str |     victory_item: str | ||||||
|     required_client_version = 0, 3, 6 |     required_client_version = 0, 3, 6 | ||||||
| 
 | 
 | ||||||
|     def __init__(self, world: MultiWorld, player: int): |     def __init__(self, multiworld: MultiWorld, player: int): | ||||||
|         super(SC2WoLWorld, self).__init__(world, player) |         super(SC2WoLWorld, self).__init__(multiworld, player) | ||||||
|         self.location_cache = [] |         self.location_cache = [] | ||||||
|         self.locked_locations = [] |         self.locked_locations = [] | ||||||
| 
 | 
 | ||||||
|  | @ -63,7 +63,7 @@ class SC2WoLWorld(World): | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|     def generate_basic(self): |     def generate_basic(self): | ||||||
|         excluded_items = get_excluded_items(self, self.multiworld, self.player) |         excluded_items = get_excluded_items(self.multiworld, self.player) | ||||||
| 
 | 
 | ||||||
|         starter_items = assign_starter_items(self.multiworld, self.player, excluded_items, self.locked_locations) |         starter_items = assign_starter_items(self.multiworld, self.player, excluded_items, self.locked_locations) | ||||||
| 
 | 
 | ||||||
|  | @ -74,7 +74,7 @@ class SC2WoLWorld(World): | ||||||
|         self.multiworld.itempool += pool |         self.multiworld.itempool += pool | ||||||
| 
 | 
 | ||||||
|     def set_rules(self): |     def set_rules(self): | ||||||
|         setup_events(self.multiworld, self.player, self.locked_locations, self.location_cache) |         setup_events(self.player, self.locked_locations, self.location_cache) | ||||||
|         self.multiworld.completion_condition[self.player] = lambda state: state.has(self.victory_item, self.player) |         self.multiworld.completion_condition[self.player] = lambda state: state.has(self.victory_item, self.player) | ||||||
| 
 | 
 | ||||||
|     def get_filler_item_name(self) -> str: |     def get_filler_item_name(self) -> str: | ||||||
|  | @ -95,7 +95,7 @@ class SC2WoLWorld(World): | ||||||
|         return slot_data |         return slot_data | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def setup_events(world: MultiWorld, player: int, locked_locations: typing.List[str], location_cache: typing.List[Location]): | def setup_events(player: int, locked_locations: typing.List[str], location_cache: typing.List[Location]): | ||||||
|     for location in location_cache: |     for location in location_cache: | ||||||
|         if location.address is None: |         if location.address is None: | ||||||
|             item = Item(location.name, ItemClassification.progression, None, player) |             item = Item(location.name, ItemClassification.progression, None, player) | ||||||
|  | @ -105,39 +105,39 @@ def setup_events(world: MultiWorld, player: int, locked_locations: typing.List[s | ||||||
|             location.place_locked_item(item) |             location.place_locked_item(item) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def get_excluded_items(self: SC2WoLWorld, world: MultiWorld, player: int) -> Set[str]: | def get_excluded_items(multiworld: MultiWorld, player: int) -> Set[str]: | ||||||
|     excluded_items: Set[str] = set() |     excluded_items: Set[str] = set() | ||||||
| 
 | 
 | ||||||
|     if get_option_value(world, player, "upgrade_bonus") == 1: |     if get_option_value(multiworld, player, "upgrade_bonus") == 1: | ||||||
|         excluded_items.add("Ultra-Capacitors") |         excluded_items.add("Ultra-Capacitors") | ||||||
|     else: |     else: | ||||||
|         excluded_items.add("Vanadium Plating") |         excluded_items.add("Vanadium Plating") | ||||||
| 
 | 
 | ||||||
|     if get_option_value(world, player, "bunker_upgrade") == 1: |     if get_option_value(multiworld, player, "bunker_upgrade") == 1: | ||||||
|         excluded_items.add("Shrike Turret") |         excluded_items.add("Shrike Turret") | ||||||
|     else: |     else: | ||||||
|         excluded_items.add("Fortified Bunker") |         excluded_items.add("Fortified Bunker") | ||||||
| 
 | 
 | ||||||
|     for item in world.precollected_items[player]: |     for item in multiworld.precollected_items[player]: | ||||||
|         excluded_items.add(item.name) |         excluded_items.add(item.name) | ||||||
| 
 | 
 | ||||||
|     excluded_items_option = getattr(world, 'excluded_items', []) |     excluded_items_option = getattr(multiworld, 'excluded_items', []) | ||||||
| 
 | 
 | ||||||
|     excluded_items.update(excluded_items_option[player].value) |     excluded_items.update(excluded_items_option[player].value) | ||||||
| 
 | 
 | ||||||
|     return excluded_items |     return excluded_items | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def assign_starter_items(world: MultiWorld, player: int, excluded_items: Set[str], locked_locations: List[str]) -> List[Item]: | def assign_starter_items(multiworld: MultiWorld, player: int, excluded_items: Set[str], locked_locations: List[str]) -> List[Item]: | ||||||
|     non_local_items = world.non_local_items[player].value |     non_local_items = multiworld.non_local_items[player].value | ||||||
|     if get_option_value(world, player, "early_unit"): |     if get_option_value(multiworld, player, "early_unit"): | ||||||
|         local_basic_unit = tuple(item for item in get_basic_units(world, player) if item not in non_local_items and item not in excluded_items) |         local_basic_unit = sorted(item for item in get_basic_units(multiworld, player) if item not in non_local_items and item not in excluded_items) | ||||||
|         if not local_basic_unit: |         if not local_basic_unit: | ||||||
|             raise Exception("At least one basic unit must be local") |             raise Exception("At least one basic unit must be local") | ||||||
| 
 | 
 | ||||||
|         # The first world should also be the starting world |         # The first world should also be the starting world | ||||||
|         first_mission = list(world.worlds[player].mission_req_table)[0] |         first_mission = list(multiworld.worlds[player].mission_req_table)[0] | ||||||
|         starting_mission_locations = get_starting_mission_locations(world, player) |         starting_mission_locations = get_starting_mission_locations(multiworld, player) | ||||||
|         if first_mission in starting_mission_locations: |         if first_mission in starting_mission_locations: | ||||||
|             first_location = starting_mission_locations[first_mission] |             first_location = starting_mission_locations[first_mission] | ||||||
|         elif first_mission == "In Utter Darkness": |         elif first_mission == "In Utter Darkness": | ||||||
|  | @ -145,28 +145,28 @@ def assign_starter_items(world: MultiWorld, player: int, excluded_items: Set[str | ||||||
|         else: |         else: | ||||||
|             first_location = first_mission + ": Victory" |             first_location = first_mission + ": Victory" | ||||||
| 
 | 
 | ||||||
|         return [assign_starter_item(world, player, excluded_items, locked_locations, first_location, local_basic_unit)] |         return [assign_starter_item(multiworld, player, excluded_items, locked_locations, first_location, local_basic_unit)] | ||||||
|     else: |     else: | ||||||
|         return [] |         return [] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def assign_starter_item(world: MultiWorld, player: int, excluded_items: Set[str], locked_locations: List[str], | def assign_starter_item(multiworld: MultiWorld, player: int, excluded_items: Set[str], locked_locations: List[str], | ||||||
|                         location: str, item_list: Tuple[str, ...]) -> Item: |                         location: str, item_list: Tuple[str, ...]) -> Item: | ||||||
| 
 | 
 | ||||||
|     item_name = world.random.choice(item_list) |     item_name = multiworld.random.choice(item_list) | ||||||
| 
 | 
 | ||||||
|     excluded_items.add(item_name) |     excluded_items.add(item_name) | ||||||
| 
 | 
 | ||||||
|     item = create_item_with_correct_settings(world, player, item_name) |     item = create_item_with_correct_settings(player, item_name) | ||||||
| 
 | 
 | ||||||
|     world.get_location(location, player).place_locked_item(item) |     multiworld.get_location(location, player).place_locked_item(item) | ||||||
| 
 | 
 | ||||||
|     locked_locations.append(location) |     locked_locations.append(location) | ||||||
| 
 | 
 | ||||||
|     return item |     return item | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def get_item_pool(world: MultiWorld, player: int, mission_req_table: Dict[str, MissionInfo], | def get_item_pool(multiworld: MultiWorld, player: int, mission_req_table: Dict[str, MissionInfo], | ||||||
|                   starter_items: List[str], excluded_items: Set[str], location_cache: List[Location]) -> List[Item]: |                   starter_items: List[str], excluded_items: Set[str], location_cache: List[Location]) -> List[Item]: | ||||||
|     pool: List[Item] = [] |     pool: List[Item] = [] | ||||||
| 
 | 
 | ||||||
|  | @ -174,18 +174,18 @@ def get_item_pool(world: MultiWorld, player: int, mission_req_table: Dict[str, M | ||||||
|     locked_items = [] |     locked_items = [] | ||||||
| 
 | 
 | ||||||
|     # YAML items |     # YAML items | ||||||
|     yaml_locked_items = get_option_set_value(world, player, 'locked_items') |     yaml_locked_items = get_option_set_value(multiworld, player, 'locked_items') | ||||||
| 
 | 
 | ||||||
|     for name, data in item_table.items(): |     for name, data in item_table.items(): | ||||||
|         if name not in excluded_items: |         if name not in excluded_items: | ||||||
|             for _ in range(data.quantity): |             for _ in range(data.quantity): | ||||||
|                 item = create_item_with_correct_settings(world, player, name) |                 item = create_item_with_correct_settings(player, name) | ||||||
|                 if name in yaml_locked_items: |                 if name in yaml_locked_items: | ||||||
|                     locked_items.append(item) |                     locked_items.append(item) | ||||||
|                 else: |                 else: | ||||||
|                     pool.append(item) |                     pool.append(item) | ||||||
| 
 | 
 | ||||||
|     existing_items = starter_items + [item for item in world.precollected_items[player]] |     existing_items = starter_items + [item for item in multiworld.precollected_items[player]] | ||||||
|     existing_names = [item.name for item in existing_items] |     existing_names = [item.name for item in existing_items] | ||||||
|     # Removing upgrades for excluded items |     # Removing upgrades for excluded items | ||||||
|     for item_name in excluded_items: |     for item_name in excluded_items: | ||||||
|  | @ -195,18 +195,18 @@ def get_item_pool(world: MultiWorld, player: int, mission_req_table: Dict[str, M | ||||||
|         for invalid_upgrade in invalid_upgrades: |         for invalid_upgrade in invalid_upgrades: | ||||||
|             pool.remove(invalid_upgrade) |             pool.remove(invalid_upgrade) | ||||||
| 
 | 
 | ||||||
|     filtered_pool = filter_items(world, player, mission_req_table, location_cache, pool, existing_items, locked_items) |     filtered_pool = filter_items(multiworld, player, mission_req_table, location_cache, pool, existing_items, locked_items) | ||||||
|     return filtered_pool |     return filtered_pool | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def fill_item_pool_with_dummy_items(self: SC2WoLWorld, world: MultiWorld, player: int, locked_locations: List[str], | def fill_item_pool_with_dummy_items(self: SC2WoLWorld, multiworld: MultiWorld, player: int, locked_locations: List[str], | ||||||
|                                     location_cache: List[Location], pool: List[Item]): |                                     location_cache: List[Location], pool: List[Item]): | ||||||
|     for _ in range(len(location_cache) - len(locked_locations) - len(pool)): |     for _ in range(len(location_cache) - len(locked_locations) - len(pool)): | ||||||
|         item = create_item_with_correct_settings(world, player, self.get_filler_item_name()) |         item = create_item_with_correct_settings(player, self.get_filler_item_name()) | ||||||
|         pool.append(item) |         pool.append(item) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def create_item_with_correct_settings(world: MultiWorld, player: int, name: str) -> Item: | def create_item_with_correct_settings(player: int, name: str) -> Item: | ||||||
|     data = item_table[name] |     data = item_table[name] | ||||||
| 
 | 
 | ||||||
|     item = Item(name, data.classification, data.code, player) |     item = Item(name, data.classification, data.code, player) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue