diff --git a/worlds/sc2/item/item_descriptions.py b/worlds/sc2/item/item_descriptions.py index 77859ae7ec37..66aedfb259b6 100644 --- a/worlds/sc2/item/item_descriptions.py +++ b/worlds/sc2/item/item_descriptions.py @@ -595,6 +595,7 @@ def _ability_desc(unit_name_plural: str, ability_name: str, ability_description: item_names.SWARM_HOST: "Siege unit that attacks by rooting in place and continually spawning Locusts.", item_names.INFESTOR: "Support caster that can move while burrowed. Can use the Fungal Growth, Parasitic Domination, and Consumption abilities.", item_names.ULTRALISK: "Massive melee attacker. Has an area-damage cleave attack.", + item_names.PYGALISK: "Miniature melee attacker. Summoned from the predator nest in large quantities and low cooldown.", item_names.SPORE_CRAWLER: "Anti-air defensive structure that can detect cloaked units.", item_names.SPINE_CRAWLER: "Anti-ground defensive structure.", item_names.CORRUPTOR: "Anti-air flying attacker specializing in taking down enemy capital ships.", @@ -639,6 +640,9 @@ def _ability_desc(unit_name_plural: str, ability_name: str, ability_description: item_names.ULTRALISK_BURROW_CHARGE: "Allows Ultralisks to burrow and charge at enemy units, knocking back and stunning units when it emerges.", item_names.ULTRALISK_TISSUE_ASSIMILATION: "Ultralisks recover health when they deal damage.", item_names.ULTRALISK_MONARCH_BLADES: "Ultralisks gain increased splash damage.", + item_names.PYGALISK_STIM: _ability_desc("Pygalisks", "Stimpack", f"temporarily increases movement and attack speed at the cost of {STIMPACK_SMALL_COST} health"), + item_names.PYGALISK_DUCAL_BLADES: "Pygalisks do splash damage.", + item_names.PYGALISK_COMBAT_CARAPACE: "Increases Pygalisk health by +25.", item_names.CORRUPTOR_CAUSTIC_SPRAY: "Allows Corruptors to use the Caustic Spray ability, which deals ramping damage to buildings over time.", item_names.CORRUPTOR_CORRUPTION: "Allows Corruptors to use the Corruption ability, which causes a target enemy unit to take increased damage.", item_names.SCOURGE_VIRULENT_SPORES: "Scourge will deal splash damage.", @@ -707,6 +711,10 @@ def _ability_desc(unit_name_plural: str, ability_name: str, ability_description: item_names.ABERRATION_PROTECTIVE_COVER: "Aberrations grant damage reduction to allied units directly beneath them.", item_names.ABERRATION_BANELING_INCUBATION: "Aberrations spawn 2 Banelings upon death.", item_names.ABERRATION_RESOURCE_EFFICIENCY: _get_resource_efficiency_desc(item_names.ABERRATION), + item_names.ABERRATION_PROGRESSIVE_BANELING_LAUNCH: inspect.cleandoc(""" + Level 1: Allows Aberrations to periodically throw generated Banelings at air targets. + Level 2: Can store up to 3 Banelings. Can consume Banelings to recharge faster. Thrown Banelings benefit from Baneling upgrades. + """), item_names.CORRUPTOR_MONSTROUS_RESILIENCE: "Corruptors gain +100 life.", item_names.CORRUPTOR_CONSTRUCT_REGENERATION: "Corruptors gain increased life regeneration.", item_names.CORRUPTOR_SCOURGE_INCUBATION: "Corruptors spawn 2 Scourge upon death (3 with Swarm Scourge).", diff --git a/worlds/sc2/item/item_names.py b/worlds/sc2/item/item_names.py index a8554aac6eba..5398d867d8fb 100644 --- a/worlds/sc2/item/item_names.py +++ b/worlds/sc2/item/item_names.py @@ -363,6 +363,7 @@ SWARM_HOST = "Swarm Host" INFESTOR = "Infestor" ULTRALISK = "Ultralisk" +PYGALISK = "Pygalisk" CORRUPTOR = "Corruptor" SCOURGE = "Scourge" BROOD_QUEEN = "Brood Queen" @@ -436,6 +437,9 @@ ULTRALISK_CHITINOUS_PLATING = "Chitinous Plating (Ultralisk)" ULTRALISK_ORGANIC_CARAPACE = "Organic Carapace (Ultralisk)" ULTRALISK_RESOURCE_EFFICIENCY = "Resource Efficiency (Ultralisk)" +PYGALISK_STIM = "Stimpack (Pygalisk)" +PYGALISK_DUCAL_BLADES = "Ducal Blades (Pygalisk)" +PYGALISK_COMBAT_CARAPACE = "Combat Carapace (Pygalisk)" CORRUPTOR_CORRUPTION = "Corruption (Corruptor)" CORRUPTOR_CAUSTIC_SPRAY = "Caustic Spray (Corruptor)" SCOURGE_VIRULENT_SPORES = "Virulent Spores (Scourge)" @@ -486,6 +490,7 @@ ABERRATION_BANELING_INCUBATION = "Baneling Incubation (Aberration)" ABERRATION_PROTECTIVE_COVER = "Protective Cover (Aberration)" ABERRATION_RESOURCE_EFFICIENCY = "Resource Efficiency (Aberration)" +ABERRATION_PROGRESSIVE_BANELING_LAUNCH = "Progressive Baneling Launch (Aberration)" CORRUPTOR_MONSTROUS_RESILIENCE = "Monstrous Resilience (Corruptor)" CORRUPTOR_CONSTRUCT_REGENERATION = "Construct Regeneration (Corruptor)" CORRUPTOR_SCOURGE_INCUBATION = "Scourge Incubation (Corruptor)" diff --git a/worlds/sc2/item/item_tables.py b/worlds/sc2/item/item_tables.py index 54710726a862..1e8b09a6e453 100644 --- a/worlds/sc2/item/item_tables.py +++ b/worlds/sc2/item/item_tables.py @@ -1200,6 +1200,9 @@ def get_full_item_list(): item_names.INFESTED_MISSILE_TURRET: ItemData(24 + SC2HOTS_ITEM_ID_OFFSET, ZergItemType.Unit, 23, SC2Race.ZERG, classification=ItemClassification.progression), + item_names.PYGALISK: + ItemData(25 + SC2HOTS_ITEM_ID_OFFSET, ZergItemType.Unit, 24, SC2Race.ZERG, + classification=ItemClassification.progression), item_names.PROGRESSIVE_ZERG_MELEE_ATTACK: ItemData(100 + SC2HOTS_ITEM_ID_OFFSET, ZergItemType.Upgrade, 0, SC2Race.ZERG, classification=ItemClassification.progression, quantity=WEAPON_ARMOR_UPGRADE_MAX_LEVEL), item_names.PROGRESSIVE_ZERG_MISSILE_ATTACK: ItemData(101 + SC2HOTS_ITEM_ID_OFFSET, ZergItemType.Upgrade, 4, SC2Race.ZERG, classification=ItemClassification.progression, quantity=WEAPON_ARMOR_UPGRADE_MAX_LEVEL), @@ -1549,6 +1552,14 @@ def get_full_item_list(): item_names.INFESTED_LIBERATOR_DEFENDER_MODE: ItemData(380 + SC2HOTS_ITEM_ID_OFFSET, ZergItemType.Mutation_5, 8, SC2Race.ZERG, parent_item=item_names.INFESTED_LIBERATOR, classification=ItemClassification.progression), + item_names.ABERRATION_PROGRESSIVE_BANELING_LAUNCH: + ItemData(381 + SC2HOTS_ITEM_ID_OFFSET, ZergItemType.Progressive, 4, SC2Race.ZERG, parent_item=item_names.ABERRATION, quantity=2), + item_names.PYGALISK_STIM: + ItemData(382 + SC2HOTS_ITEM_ID_OFFSET, ZergItemType.Mutation_5, 9, SC2Race.ZERG, parent_item=item_names.PYGALISK), + item_names.PYGALISK_DUCAL_BLADES: + ItemData(383 + SC2HOTS_ITEM_ID_OFFSET, ZergItemType.Mutation_5, 10, SC2Race.ZERG, parent_item=item_names.PYGALISK), + item_names.PYGALISK_COMBAT_CARAPACE: + ItemData(384 + SC2HOTS_ITEM_ID_OFFSET, ZergItemType.Mutation_5, 11, SC2Race.ZERG, parent_item=item_names.PYGALISK), item_names.KERRIGAN_KINETIC_BLAST: ItemData(400 + SC2HOTS_ITEM_ID_OFFSET, ZergItemType.Ability, 0, SC2Race.ZERG, classification=ItemClassification.progression), item_names.KERRIGAN_HEROIC_FORTITUDE: ItemData(401 + SC2HOTS_ITEM_ID_OFFSET, ZergItemType.Ability, 1, SC2Race.ZERG, classification=ItemClassification.progression), diff --git a/worlds/sc2/locations.py b/worlds/sc2/locations.py index c5672e13db02..a47fc0a3e63a 100644 --- a/worlds/sc2/locations.py +++ b/worlds/sc2/locations.py @@ -2922,6 +2922,97 @@ def get_locations(world: Optional['SC2World']) -> Tuple[LocationData, ...]: and logic.protoss_common_unit(state) and (logic.protoss_competent_comp(state) or state.has(item_names.OBSERVER, player))) ), + 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) @@ -4979,6 +5070,30 @@ def get_locations(world: Optional['SC2World']) -> Tuple[LocationData, ...]: make_location_data(SC2Mission.LAST_STAND_Z.mission_name, "1.5 Billion Zerg", SC2_RACESWAP_LOC_ID_OFFSET + 11805, LocationType.VANILLA, logic.zerg_last_stand_requirement ), + make_location_data(SC2Mission.FORBIDDEN_WEAPON_T.mission_name, "Victory", SC2_RACESWAP_LOC_ID_OFFSET + 11900, LocationType.VICTORY, + logic.terran_beats_protoss_deathball + ), + make_location_data(SC2Mission.FORBIDDEN_WEAPON_T.mission_name, "South Solarite", SC2_RACESWAP_LOC_ID_OFFSET + 11901, LocationType.VANILLA, + logic.terran_beats_protoss_deathball + ), + make_location_data(SC2Mission.FORBIDDEN_WEAPON_T.mission_name, "North Solarite", SC2_RACESWAP_LOC_ID_OFFSET + 11902, LocationType.VANILLA, + logic.terran_beats_protoss_deathball + ), + make_location_data(SC2Mission.FORBIDDEN_WEAPON_T.mission_name, "Northwest Solarite", SC2_RACESWAP_LOC_ID_OFFSET + 11903, LocationType.VANILLA, + logic.terran_beats_protoss_deathball + ), + make_location_data(SC2Mission.FORBIDDEN_WEAPON_Z.mission_name, "Victory", SC2_RACESWAP_LOC_ID_OFFSET + 12000, LocationType.VICTORY, + logic.zerg_competent_comp_competent_aa + ), + make_location_data(SC2Mission.FORBIDDEN_WEAPON_Z.mission_name, "South Solarite", SC2_RACESWAP_LOC_ID_OFFSET + 12001, LocationType.VANILLA, + logic.zerg_competent_comp_competent_aa + ), + make_location_data(SC2Mission.FORBIDDEN_WEAPON_Z.mission_name, "North Solarite", SC2_RACESWAP_LOC_ID_OFFSET + 12002, LocationType.VANILLA, + logic.zerg_competent_comp_competent_aa + ), + make_location_data(SC2Mission.FORBIDDEN_WEAPON_Z.mission_name, "Northwest Solarite", SC2_RACESWAP_LOC_ID_OFFSET + 12003, LocationType.VANILLA, + logic.zerg_competent_comp_competent_aa + ), ] beat_events = [] diff --git a/worlds/sc2/mission_tables.py b/worlds/sc2/mission_tables.py index c992c2435cdc..8e0f04c42a17 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 (Terran)", SC2Campaign.WOL, "Artifact", SC2Race.TERRAN, MissionPools.MEDIUM, "ap_the_dig", MissionFlag.Terran|MissionFlag.TimedDefense|MissionFlag.VsProtoss|MissionFlag.HasRaceSwap - 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 @@ -167,7 +167,7 @@ def __init__(self, mission_id: int, name: str, campaign: SC2Campaign, area: str, BROTHERS_IN_ARMS = 57, "Brothers in Arms (Protoss)", SC2Campaign.LOTV, "Korhal", SC2Race.PROTOSS, MissionPools.MEDIUM, "ap_brothers_in_arms", MissionFlag.Protoss|MissionFlag.VanillaSoa|MissionFlag.VsTerran|MissionFlag.AiTerranAlly|MissionFlag.HasRaceSwap AMON_S_REACH = 58, "Amon's Reach (Protoss)", SC2Campaign.LOTV, "Shakuras", SC2Race.PROTOSS, MissionPools.EASY, "ap_amon_s_reach", MissionFlag.Protoss|MissionFlag.VanillaSoa|MissionFlag.VsZerg|MissionFlag.HasRaceSwap LAST_STAND = 59, "Last Stand (Protoss)", SC2Campaign.LOTV, "Shakuras", SC2Race.PROTOSS, MissionPools.HARD, "ap_last_stand", MissionFlag.Protoss|MissionFlag.VanillaSoa|MissionFlag.TimedDefense|MissionFlag.VsZerg|MissionFlag.HasRaceSwap - FORBIDDEN_WEAPON = 60, "Forbidden Weapon", SC2Campaign.LOTV, "Purifier", SC2Race.PROTOSS, MissionPools.MEDIUM, "ap_forbidden_weapon", MissionFlag.Protoss|MissionFlag.VanillaSoa|MissionFlag.Countdown|MissionFlag.VsProtoss + FORBIDDEN_WEAPON = 60, "Forbidden Weapon (Protoss)", SC2Campaign.LOTV, "Purifier", SC2Race.PROTOSS, MissionPools.MEDIUM, "ap_forbidden_weapon", MissionFlag.Protoss|MissionFlag.VanillaSoa|MissionFlag.Countdown|MissionFlag.VsProtoss|MissionFlag.HasRaceSwap TEMPLE_OF_UNIFICATION = 61, "Temple of Unification", SC2Campaign.LOTV, "Ulnar", SC2Race.PROTOSS, MissionPools.MEDIUM, "ap_temple_of_unification", MissionFlag.Protoss|MissionFlag.VanillaSoa|MissionFlag.VsTP THE_INFINITE_CYCLE = 62, "The Infinite Cycle", SC2Campaign.LOTV, "Ulnar", SC2Race.ANY, MissionPools.HARD, "ap_the_infinite_cycle", MissionFlag.Protoss|MissionFlag.Kerrigan|MissionFlag.NoBuild|MissionFlag.VsTP HARBINGER_OF_OBLIVION = 63, "Harbinger of Oblivion", SC2Campaign.LOTV, "Ulnar", SC2Race.PROTOSS, MissionPools.MEDIUM, "ap_harbinger_of_oblivion", MissionFlag.Protoss|MissionFlag.VanillaSoa|MissionFlag.Countdown|MissionFlag.VsTP|MissionFlag.AiZergAlly @@ -214,7 +214,8 @@ def __init__(self, mission_id: int, name: str, campaign: SC2Campaign, area: str, 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 THE_DIG_Z = 100, "The Dig (Zerg)", SC2Campaign.WOL, "Artifact", SC2Race.ZERG, MissionPools.MEDIUM, "ap_the_dig", MissionFlag.Zerg|MissionFlag.TimedDefense|MissionFlag.VsProtoss|MissionFlag.RaceSwap THE_DIG_P = 101, "The Dig (Protoss)", SC2Campaign.WOL, "Artifact", SC2Race.PROTOSS, MissionPools.MEDIUM, "ap_the_dig", MissionFlag.Protoss|MissionFlag.TimedDefense|MissionFlag.VsProtoss|MissionFlag.RaceSwap - # 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 @@ -297,7 +298,8 @@ def __init__(self, mission_id: int, name: str, campaign: SC2Campaign, area: str, AMON_S_REACH_Z = 199, "Amon's Reach (Zerg)", SC2Campaign.LOTV, "Shakuras", SC2Race.ZERG, MissionPools.EASY, "ap_amon_s_reach", MissionFlag.Zerg|MissionFlag.VsZerg|MissionFlag.RaceSwap LAST_STAND_T = 200, "Last Stand (Terran)", SC2Campaign.LOTV, "Shakuras", SC2Race.TERRAN, MissionPools.HARD, "ap_last_stand", MissionFlag.Terran|MissionFlag.TimedDefense|MissionFlag.VsZerg|MissionFlag.RaceSwap LAST_STAND_Z = 201, "Last Stand (Zerg)", SC2Campaign.LOTV, "Shakuras", SC2Race.ZERG, MissionPools.HARD, "ap_last_stand", MissionFlag.Zerg|MissionFlag.TimedDefense|MissionFlag.VsZerg|MissionFlag.RaceSwap - # 202/203 - Forbidden Weapon + FORBIDDEN_WEAPON_T = 202, "Forbidden Weapon (Terran)", SC2Campaign.LOTV, "Purifier", SC2Race.TERRAN, MissionPools.MEDIUM, "ap_forbidden_weapon", MissionFlag.Terran|MissionFlag.Countdown|MissionFlag.VsProtoss|MissionFlag.RaceSwap + FORBIDDEN_WEAPON_Z = 203, "Forbidden Weapon (Zerg)", SC2Campaign.LOTV, "Purifier", SC2Race.ZERG, MissionPools.MEDIUM, "ap_forbidden_weapon", MissionFlag.Zerg|MissionFlag.Countdown|MissionFlag.VsProtoss|MissionFlag.RaceSwap # 204/205 - Temple of Unification # 206/207 - The Infinite Cycle # 208/209 - Harbinger of Oblivion 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)