diff --git a/worlds/sc2/locations.py b/worlds/sc2/locations.py index 7e56d21b4ad3..123fa784ccc7 100644 --- a/worlds/sc2/locations.py +++ b/worlds/sc2/locations.py @@ -2783,6 +2783,97 @@ def get_locations(world: Optional['SC2World']) -> Tuple[LocationData, ...]: make_location_data(SC2Mission.SMASH_AND_GRAB_P.mission_name, "Defeat Kerrigan", SC2_RACESWAP_LOC_ID_OFFSET + 1607, LocationType.MASTERY, logic.protoss_competent_comp ), + make_location_data(SC2Mission.THE_MOEBIUS_FACTOR_Z.mission_name, "Victory", SC2_RACESWAP_LOC_ID_OFFSET + 1900, LocationType.VICTORY, + lambda state: ( + logic.zerg_basic_kerriganless_anti_air(state) + and (logic.zerg_versatile_air(state) + or state.has_any({item_names.YGGDRASIL, item_names.OVERLORD_VENTRAL_SACS, item_names.NYDUS_WORM}, player) + and logic.zerg_common_unit(state))) + ), + make_location_data(SC2Mission.THE_MOEBIUS_FACTOR_Z.mission_name, "1st Data Core", SC2_RACESWAP_LOC_ID_OFFSET + 1901, LocationType.VANILLA), + make_location_data(SC2Mission.THE_MOEBIUS_FACTOR_Z.mission_name, "2nd Data Core", SC2_RACESWAP_LOC_ID_OFFSET + 1902, LocationType.VANILLA, + lambda state: ( + logic.zerg_versatile_air(state) + or state.has_any({item_names.YGGDRASIL, item_names.OVERLORD_VENTRAL_SACS, item_names.NYDUS_WORM}, player) + and logic.zerg_common_unit(state)) + ), + make_location_data(SC2Mission.THE_MOEBIUS_FACTOR_Z.mission_name, "South Rescue", SC2_RACESWAP_LOC_ID_OFFSET + 1903, LocationType.EXTRA, + lambda state: state.has_any({item_names.YGGDRASIL, item_names.OVERLORD_VENTRAL_SACS, item_names.NYDUS_WORM}, player) + ), + make_location_data(SC2Mission.THE_MOEBIUS_FACTOR_Z.mission_name, "Wall Rescue", SC2_RACESWAP_LOC_ID_OFFSET + 1904, LocationType.EXTRA, + lambda state: state.has_any({item_names.YGGDRASIL, item_names.OVERLORD_VENTRAL_SACS, item_names.NYDUS_WORM}, player) + ), + make_location_data(SC2Mission.THE_MOEBIUS_FACTOR_Z.mission_name, "Mid Rescue", SC2_RACESWAP_LOC_ID_OFFSET + 1905, LocationType.EXTRA, + lambda state: state.has_any({item_names.YGGDRASIL, item_names.OVERLORD_VENTRAL_SACS, item_names.NYDUS_WORM}, player) + ), + make_location_data(SC2Mission.THE_MOEBIUS_FACTOR_Z.mission_name, "Nydus Roof Rescue", SC2_RACESWAP_LOC_ID_OFFSET + 1906, LocationType.EXTRA, + lambda state: state.has_any({item_names.YGGDRASIL, item_names.OVERLORD_VENTRAL_SACS, item_names.NYDUS_WORM}, player) + ), + make_location_data(SC2Mission.THE_MOEBIUS_FACTOR_Z.mission_name, "Alive Inside Rescue", SC2_RACESWAP_LOC_ID_OFFSET + 1907, LocationType.EXTRA, + lambda state: state.has_any({item_names.YGGDRASIL, item_names.OVERLORD_VENTRAL_SACS, item_names.NYDUS_WORM}, player) + ), + make_location_data(SC2Mission.THE_MOEBIUS_FACTOR_Z.mission_name, "Brutalisk", SC2_RACESWAP_LOC_ID_OFFSET + 1908, LocationType.VANILLA, + lambda state: ( + logic.zerg_basic_kerriganless_anti_air(state) + and (logic.zerg_versatile_air(state) + or state.has_any({item_names.YGGDRASIL, item_names.OVERLORD_VENTRAL_SACS, item_names.NYDUS_WORM}, player) + and logic.zerg_common_unit(state))) + ), + make_location_data(SC2Mission.THE_MOEBIUS_FACTOR_Z.mission_name, "3rd Data Core", SC2_RACESWAP_LOC_ID_OFFSET + 1909, LocationType.VANILLA, + lambda state: ( + logic.zerg_basic_kerriganless_anti_air(state) + and (logic.zerg_versatile_air(state) + or state.has_any({item_names.YGGDRASIL, item_names.OVERLORD_VENTRAL_SACS, item_names.NYDUS_WORM}, player) + and logic.zerg_common_unit(state))) + ), + make_location_data(SC2Mission.THE_MOEBIUS_FACTOR_P.mission_name, "Victory", SC2_RACESWAP_LOC_ID_OFFSET + 2000, LocationType.VICTORY, + lambda state: ( + logic.protoss_basic_anti_air(state) + and (logic.protoss_fleet(state) + or state.has(item_names.WARP_PRISM, player) + and logic.protoss_common_unit(state))) + ), + make_location_data(SC2Mission.THE_MOEBIUS_FACTOR_P.mission_name, "1st Data Core", SC2_RACESWAP_LOC_ID_OFFSET + 2001, LocationType.VANILLA), + make_location_data(SC2Mission.THE_MOEBIUS_FACTOR_P.mission_name, "2nd Data Core", SC2_RACESWAP_LOC_ID_OFFSET + 2002, LocationType.VANILLA, + lambda state: ( + logic.protoss_fleet(state) + or (state.has(item_names.WARP_PRISM, player) + and logic.protoss_common_unit(state))) + ), + make_location_data(SC2Mission.THE_MOEBIUS_FACTOR_P.mission_name, "South Rescue", SC2_RACESWAP_LOC_ID_OFFSET + 2003, LocationType.EXTRA, + lambda state: state.has(item_names.WARP_PRISM, player) + and (adv_tactics or state.has(item_names.PROGRESSIVE_WARP_RELOCATE, player)) + ), + make_location_data(SC2Mission.THE_MOEBIUS_FACTOR_P.mission_name, "Wall Rescue", SC2_RACESWAP_LOC_ID_OFFSET + 2004, LocationType.EXTRA, + lambda state: state.has(item_names.WARP_PRISM, player) + and (adv_tactics or state.has(item_names.PROGRESSIVE_WARP_RELOCATE, player)) + ), + make_location_data(SC2Mission.THE_MOEBIUS_FACTOR_P.mission_name, "Mid Rescue", SC2_RACESWAP_LOC_ID_OFFSET + 2005, LocationType.EXTRA, + lambda state: state.has(item_names.WARP_PRISM, player) + and (adv_tactics or state.has(item_names.PROGRESSIVE_WARP_RELOCATE, player)) + ), + make_location_data(SC2Mission.THE_MOEBIUS_FACTOR_P.mission_name, "Nydus Roof Rescue", SC2_RACESWAP_LOC_ID_OFFSET + 2006, LocationType.EXTRA, + lambda state: state.has(item_names.WARP_PRISM, player) + and (adv_tactics or state.has(item_names.PROGRESSIVE_WARP_RELOCATE, player)) + ), + make_location_data(SC2Mission.THE_MOEBIUS_FACTOR_P.mission_name, "Alive Inside Rescue", SC2_RACESWAP_LOC_ID_OFFSET + 2007, LocationType.EXTRA, + lambda state: state.has(item_names.WARP_PRISM, player) + and (adv_tactics or state.has(item_names.PROGRESSIVE_WARP_RELOCATE, player)) + ), + make_location_data(SC2Mission.THE_MOEBIUS_FACTOR_P.mission_name, "Brutalisk", SC2_RACESWAP_LOC_ID_OFFSET + 2008, LocationType.VANILLA, + lambda state: ( + logic.protoss_basic_anti_air(state) + and (logic.protoss_fleet(state) + or state.has(item_names.WARP_PRISM, player) + and logic.protoss_common_unit(state))) + ), + make_location_data(SC2Mission.THE_MOEBIUS_FACTOR_P.mission_name, "3rd Data Core", SC2_RACESWAP_LOC_ID_OFFSET + 2009, LocationType.VANILLA, + lambda state: ( + logic.protoss_basic_anti_air(state) + and (logic.protoss_fleet(state) + or state.has(item_names.WARP_PRISM, player) + and logic.protoss_common_unit(state))) + ), make_location_data(SC2Mission.SUPERNOVA_Z.mission_name, "Victory", SC2_RACESWAP_LOC_ID_OFFSET + 2100, LocationType.VICTORY, lambda state: ( logic.zerg_competent_comp_competent_aa(state) diff --git a/worlds/sc2/mission_tables.py b/worlds/sc2/mission_tables.py index 03e6c4e7ce78..25a8f80e1176 100644 --- a/worlds/sc2/mission_tables.py +++ b/worlds/sc2/mission_tables.py @@ -109,7 +109,7 @@ def __init__(self, mission_id: int, name: str, campaign: SC2Campaign, area: str, HAVENS_FALL = 7, "Haven's Fall (Terran)", SC2Campaign.WOL, "Colonist", SC2Race.TERRAN, MissionPools.MEDIUM, "ap_havens_fall", MissionFlag.Terran|MissionFlag.VsZerg|MissionFlag.HasRaceSwap SMASH_AND_GRAB = 8, "Smash and Grab (Terran)", SC2Campaign.WOL, "Artifact", SC2Race.TERRAN, MissionPools.EASY, "ap_smash_and_grab", MissionFlag.Terran|MissionFlag.Countdown|MissionFlag.VsPZ|MissionFlag.HasRaceSwap THE_DIG = 9, "The Dig", SC2Campaign.WOL, "Artifact", SC2Race.TERRAN, MissionPools.MEDIUM, "ap_the_dig", MissionFlag.Terran|MissionFlag.TimedDefense|MissionFlag.VsProtoss - THE_MOEBIUS_FACTOR = 10, "The Moebius Factor", SC2Campaign.WOL, "Artifact", SC2Race.TERRAN, MissionPools.MEDIUM, "ap_the_moebius_factor", MissionFlag.Terran|MissionFlag.Countdown|MissionFlag.VsZerg + THE_MOEBIUS_FACTOR = 10, "The Moebius Factor (Terran)", SC2Campaign.WOL, "Artifact", SC2Race.TERRAN, MissionPools.MEDIUM, "ap_the_moebius_factor", MissionFlag.Terran|MissionFlag.Countdown|MissionFlag.VsZerg|MissionFlag.HasRaceSwap SUPERNOVA = 11, "Supernova (Terran)", SC2Campaign.WOL, "Artifact", SC2Race.TERRAN, MissionPools.HARD, "ap_supernova", MissionFlag.Terran|MissionFlag.Countdown|MissionFlag.VsProtoss|MissionFlag.HasRaceSwap MAW_OF_THE_VOID = 12, "Maw of the Void (Terran)", SC2Campaign.WOL, "Artifact", SC2Race.TERRAN, MissionPools.HARD, "ap_maw_of_the_void", MissionFlag.Terran|MissionFlag.VsProtoss|MissionFlag.HasRaceSwap DEVILS_PLAYGROUND = 13, "Devil's Playground (Terran)", SC2Campaign.WOL, "Covert", SC2Race.TERRAN, MissionPools.EASY, "ap_devils_playground", MissionFlag.Terran|MissionFlag.VsZerg|MissionFlag.HasRaceSwap @@ -213,7 +213,8 @@ def __init__(self, mission_id: int, name: str, campaign: SC2Campaign, area: str, SMASH_AND_GRAB_Z = 98, "Smash and Grab (Zerg)", SC2Campaign.WOL, "Artifact", SC2Race.ZERG, MissionPools.MEDIUM, "ap_smash_and_grab", MissionFlag.Zerg|MissionFlag.Countdown|MissionFlag.VsPZ|MissionFlag.RaceSwap SMASH_AND_GRAB_P = 99, "Smash and Grab (Protoss)", SC2Campaign.WOL, "Artifact", SC2Race.PROTOSS, MissionPools.EASY, "ap_smash_and_grab", MissionFlag.Protoss|MissionFlag.Countdown|MissionFlag.VsPZ|MissionFlag.RaceSwap # 100/101 - The Dig - # 102/103 - Moebius Factor + THE_MOEBIUS_FACTOR_Z = 102, "The Moebius Factor (Zerg)", SC2Campaign.WOL, "Artifact", SC2Race.ZERG, MissionPools.MEDIUM, "ap_the_moebius_factor", MissionFlag.Zerg|MissionFlag.Countdown|MissionFlag.VsZerg|MissionFlag.RaceSwap + THE_MOEBIUS_FACTOR_P = 103, "The Moebius Factor (Protoss)", SC2Campaign.WOL, "Artifact", SC2Race.PROTOSS, MissionPools.MEDIUM, "ap_the_moebius_factor", MissionFlag.Protoss|MissionFlag.Countdown|MissionFlag.VsZerg|MissionFlag.RaceSwap SUPERNOVA_Z = 104, "Supernova (Zerg)", SC2Campaign.WOL, "Artifact", SC2Race.ZERG, MissionPools.HARD, "ap_supernova", MissionFlag.Zerg|MissionFlag.Countdown|MissionFlag.VsProtoss|MissionFlag.RaceSwap SUPERNOVA_P = 105, "Supernova (Protoss)", SC2Campaign.WOL, "Artifact", SC2Race.PROTOSS, MissionPools.HARD, "ap_supernova", MissionFlag.Protoss|MissionFlag.Countdown|MissionFlag.VsProtoss|MissionFlag.RaceSwap MAW_OF_THE_VOID_Z = 106, "Maw of the Void (Zerg)", SC2Campaign.WOL, "Artifact", SC2Race.ZERG, MissionPools.HARD, "ap_maw_of_the_void", MissionFlag.Zerg|MissionFlag.VsProtoss|MissionFlag.RaceSwap diff --git a/worlds/sc2/rules.py b/worlds/sc2/rules.py index e0588e4c1b5f..108d59c6e9ef 100644 --- a/worlds/sc2/rules.py +++ b/worlds/sc2/rules.py @@ -948,6 +948,21 @@ def zerg_basic_air_to_air(self, state: CollectionState) -> bool: ) ) + def zerg_basic_air_to_ground(self, state: CollectionState) -> bool: + return ( + state.has_any({ + item_names.MUTALISK, item_names.INFESTED_BANSHEE + }, self.player) + or self.morph_guardian(state) + or self.morph_brood_lord(state) + or ( + self.morph_devourer(state) and state.has(item_names.DEVOURER_PRESCIENT_SPORES, self.player) + ) + ) + + def zerg_versatile_air(self, state: CollectionState) -> bool: + return self.zerg_basic_air_to_air(state) and self.zerg_basic_air_to_ground(state) + def zerg_infested_tank_with_ammo(self, state: CollectionState) -> bool: return ( state.has(item_names.INFESTED_SIEGE_TANK, self.player)