Adds HotS, LotV and NCO campaigns to SC2 game. The world's name has changed to reflect that (it's not only Wings of Liberty now) The client was patched in a way that can still join to games generated prior this change --------- Co-authored-by: Magnemania <magnemight@gmail.com> Co-authored-by: EnvyDragon <138727357+EnvyDragon@users.noreply.github.com> Co-authored-by: Matthew <matthew.marinets@gmail.com> Co-authored-by: hopop201 <benjy.hopop201@gmail.com> Co-authored-by: Salzkorn <salzkitty@gmail.com> Co-authored-by: genderdruid <pallyoffail@gmail.com> Co-authored-by: MadiMadsen <137329235+MadiMadsen@users.noreply.github.com> Co-authored-by: neocerber <neocerber@gmail.com> Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> Co-authored-by: Fabian Dill <Berserker66@users.noreply.github.com>
		
			
				
	
	
		
			953 lines
		
	
	
		
			47 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			953 lines
		
	
	
		
			47 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
from typing import Set
 | 
						|
 | 
						|
from BaseClasses import  CollectionState
 | 
						|
from .Options import get_option_value, RequiredTactics, kerrigan_unit_available, AllInMap, \
 | 
						|
    GrantStoryTech, GrantStoryLevels, TakeOverAIAllies, SpearOfAdunAutonomouslyCastAbilityPresence, \
 | 
						|
    get_enabled_campaigns, MissionOrder
 | 
						|
from .Items import get_basic_units, defense_ratings, zerg_defense_ratings, kerrigan_actives, air_defense_ratings, \
 | 
						|
    kerrigan_levels, get_full_item_list
 | 
						|
from .MissionTables import SC2Race, SC2Campaign
 | 
						|
from . import ItemNames
 | 
						|
from worlds.AutoWorld import World
 | 
						|
 | 
						|
 | 
						|
class SC2Logic:
 | 
						|
 | 
						|
    def lock_any_item(self, state: CollectionState, items: Set[str]) -> bool:
 | 
						|
        """
 | 
						|
        Guarantees that at least one of these items will remain in the world. Doesn't affect placement.
 | 
						|
        Needed for cases when the dynamic pool filtering could remove all the item prerequisites
 | 
						|
        :param state:
 | 
						|
        :param items:
 | 
						|
        :return:
 | 
						|
        """
 | 
						|
        return self.is_item_placement(state) \
 | 
						|
            or state.has_any(items, self.player)
 | 
						|
 | 
						|
    def is_item_placement(self, state):
 | 
						|
        """
 | 
						|
        Tells if it's item placement or item pool filter
 | 
						|
        :param state:
 | 
						|
        :return: True for item placement, False for pool filter
 | 
						|
        """
 | 
						|
        # has_group with count = 0 is always true for item placement and always false for SC2 item filtering
 | 
						|
        return state.has_group("Missions", self.player, 0)
 | 
						|
 | 
						|
    # WoL
 | 
						|
    def terran_common_unit(self, state: CollectionState) -> bool:
 | 
						|
        return state.has_any(self.basic_terran_units, self.player)
 | 
						|
 | 
						|
    def terran_early_tech(self, state: CollectionState):
 | 
						|
        """
 | 
						|
        Basic combat unit that can be deployed quickly from mission start
 | 
						|
        :param state
 | 
						|
        :return:
 | 
						|
        """
 | 
						|
        return (
 | 
						|
                state.has_any({ItemNames.MARINE, ItemNames.FIREBAT, ItemNames.MARAUDER, ItemNames.REAPER, ItemNames.HELLION}, self.player)
 | 
						|
                or (self.advanced_tactics and state.has_any({ItemNames.GOLIATH, ItemNames.DIAMONDBACK, ItemNames.VIKING, ItemNames.BANSHEE}, self.player))
 | 
						|
        )
 | 
						|
 | 
						|
    def terran_air(self, state: CollectionState) -> bool:
 | 
						|
        """
 | 
						|
        Air units or drops on advanced tactics
 | 
						|
        :param state:
 | 
						|
        :return:
 | 
						|
        """
 | 
						|
        return (state.has_any({ItemNames.VIKING, ItemNames.WRAITH, ItemNames.BANSHEE, ItemNames.BATTLECRUISER}, self.player) or self.advanced_tactics
 | 
						|
                and state.has_any({ItemNames.HERCULES, ItemNames.MEDIVAC}, self.player) and self.terran_common_unit(state)
 | 
						|
        )
 | 
						|
 | 
						|
    def terran_air_anti_air(self, state: CollectionState) -> bool:
 | 
						|
        """
 | 
						|
        Air-to-air
 | 
						|
        :param state:
 | 
						|
        :return:
 | 
						|
        """
 | 
						|
        return (
 | 
						|
            state.has(ItemNames.VIKING, self.player)
 | 
						|
            or state.has_all({ItemNames.WRAITH, ItemNames.WRAITH_ADVANCED_LASER_TECHNOLOGY}, self.player)
 | 
						|
            or state.has_all({ItemNames.BATTLECRUISER, ItemNames.BATTLECRUISER_ATX_LASER_BATTERY}, self.player)
 | 
						|
            or self.advanced_tactics and state.has_any({ItemNames.WRAITH, ItemNames.VALKYRIE, ItemNames.BATTLECRUISER}, self.player)
 | 
						|
        )
 | 
						|
 | 
						|
    def terran_competent_ground_to_air(self, state: CollectionState) -> bool:
 | 
						|
        """
 | 
						|
        Ground-to-air
 | 
						|
        :param state:
 | 
						|
        :return:
 | 
						|
        """
 | 
						|
        return (
 | 
						|
            state.has(ItemNames.GOLIATH, self.player)
 | 
						|
            or state.has(ItemNames.MARINE, self.player) and self.terran_bio_heal(state)
 | 
						|
            or self.advanced_tactics and state.has(ItemNames.CYCLONE, self.player)
 | 
						|
        )
 | 
						|
 | 
						|
    def terran_competent_anti_air(self, state: CollectionState) -> bool:
 | 
						|
        """
 | 
						|
        Good AA unit
 | 
						|
        :param state:
 | 
						|
        :return:
 | 
						|
        """
 | 
						|
        return (
 | 
						|
            self.terran_competent_ground_to_air(state)
 | 
						|
            or self.terran_air_anti_air(state)
 | 
						|
        )
 | 
						|
 | 
						|
    def welcome_to_the_jungle_requirement(self, state: CollectionState) -> bool:
 | 
						|
        """
 | 
						|
        Welcome to the Jungle requirements - able to deal with Scouts, Void Rays, Zealots and Stalkers
 | 
						|
        :param state:
 | 
						|
        :return:
 | 
						|
        """
 | 
						|
        return (
 | 
						|
            self.terran_common_unit(state)
 | 
						|
            and self.terran_competent_ground_to_air(state)
 | 
						|
        ) or (
 | 
						|
            self.advanced_tactics
 | 
						|
            and state.has_any({ItemNames.MARINE, ItemNames.VULTURE}, self.player)
 | 
						|
            and self.terran_air_anti_air(state)
 | 
						|
        )
 | 
						|
 | 
						|
    def terran_basic_anti_air(self, state: CollectionState) -> bool:
 | 
						|
        """
 | 
						|
        Basic AA to deal with few air units
 | 
						|
        :param state:
 | 
						|
        :return:
 | 
						|
        """
 | 
						|
        return (
 | 
						|
            state.has_any({
 | 
						|
                ItemNames.MISSILE_TURRET, ItemNames.THOR, ItemNames.WAR_PIGS, ItemNames.SPARTAN_COMPANY,
 | 
						|
                ItemNames.HELS_ANGELS, ItemNames.BATTLECRUISER, ItemNames.MARINE, ItemNames.WRAITH,
 | 
						|
                ItemNames.VALKYRIE, ItemNames.CYCLONE, ItemNames.WINGED_NIGHTMARES, ItemNames.BRYNHILDS
 | 
						|
            }, self.player)
 | 
						|
            or self.terran_competent_anti_air(state)
 | 
						|
            or self.advanced_tactics and state.has_any({ItemNames.GHOST, ItemNames.SPECTRE, ItemNames.WIDOW_MINE, ItemNames.LIBERATOR}, self.player)
 | 
						|
        )
 | 
						|
 | 
						|
    def terran_defense_rating(self, state: CollectionState, zerg_enemy: bool, air_enemy: bool = True) -> int:
 | 
						|
        """
 | 
						|
        Ability to handle defensive missions
 | 
						|
        :param state:
 | 
						|
        :param zerg_enemy:
 | 
						|
        :param air_enemy:
 | 
						|
        :return:
 | 
						|
        """
 | 
						|
        defense_score = sum((defense_ratings[item] for item in defense_ratings if state.has(item, self.player)))
 | 
						|
        # Manned Bunker
 | 
						|
        if state.has_any({ItemNames.MARINE, ItemNames.MARAUDER}, self.player) and state.has(ItemNames.BUNKER, self.player):
 | 
						|
            defense_score += 3
 | 
						|
        elif zerg_enemy and state.has(ItemNames.FIREBAT, self.player) and state.has(ItemNames.BUNKER, self.player):
 | 
						|
            defense_score += 2
 | 
						|
        # Siege Tank upgrades
 | 
						|
        if state.has_all({ItemNames.SIEGE_TANK, ItemNames.SIEGE_TANK_MAELSTROM_ROUNDS}, self.player):
 | 
						|
            defense_score += 2
 | 
						|
        if state.has_all({ItemNames.SIEGE_TANK, ItemNames.SIEGE_TANK_GRADUATING_RANGE}, self.player):
 | 
						|
            defense_score += 1
 | 
						|
        # Widow Mine upgrade
 | 
						|
        if state.has_all({ItemNames.WIDOW_MINE, ItemNames.WIDOW_MINE_CONCEALMENT}, self.player):
 | 
						|
            defense_score += 1
 | 
						|
        # Viking with splash
 | 
						|
        if state.has_all({ItemNames.VIKING, ItemNames.VIKING_SHREDDER_ROUNDS}, self.player):
 | 
						|
            defense_score += 2
 | 
						|
 | 
						|
        # General enemy-based rules
 | 
						|
        if zerg_enemy:
 | 
						|
            defense_score += sum((zerg_defense_ratings[item] for item in zerg_defense_ratings if state.has(item, self.player)))
 | 
						|
        if air_enemy:
 | 
						|
            defense_score += sum((air_defense_ratings[item] for item in air_defense_ratings if state.has(item, self.player)))
 | 
						|
        if air_enemy and zerg_enemy and state.has(ItemNames.VALKYRIE, self.player):
 | 
						|
            # Valkyries shred mass Mutas, most common air enemy that's massed in these cases
 | 
						|
            defense_score += 2
 | 
						|
        # Advanced Tactics bumps defense rating requirements down by 2
 | 
						|
        if self.advanced_tactics:
 | 
						|
            defense_score += 2
 | 
						|
        return defense_score
 | 
						|
 | 
						|
    def terran_competent_comp(self, state: CollectionState) -> bool:
 | 
						|
        """
 | 
						|
        Ability to deal with most of hard missions
 | 
						|
        :param state:
 | 
						|
        :return:
 | 
						|
        """
 | 
						|
        return (
 | 
						|
            (
 | 
						|
                (state.has_any({ItemNames.MARINE, ItemNames.MARAUDER}, self.player) and self.terran_bio_heal(state))
 | 
						|
                or state.has_any({ItemNames.THOR, ItemNames.BANSHEE, ItemNames.SIEGE_TANK}, self.player)
 | 
						|
                or state.has_all({ItemNames.LIBERATOR, ItemNames.LIBERATOR_RAID_ARTILLERY}, self.player)
 | 
						|
            )
 | 
						|
            and self.terran_competent_anti_air(state)
 | 
						|
        ) or (
 | 
						|
            state.has(ItemNames.BATTLECRUISER, self.player) and self.terran_common_unit(state)
 | 
						|
        )
 | 
						|
 | 
						|
    def great_train_robbery_train_stopper(self, state: CollectionState) -> bool:
 | 
						|
        """
 | 
						|
        Ability to deal with trains (moving target with a lot of HP)
 | 
						|
        :param state:
 | 
						|
        :return:
 | 
						|
        """
 | 
						|
        return (
 | 
						|
            state.has_any({ItemNames.SIEGE_TANK, ItemNames.DIAMONDBACK, ItemNames.MARAUDER, ItemNames.CYCLONE, ItemNames.BANSHEE}, self.player)
 | 
						|
            or self.advanced_tactics
 | 
						|
            and (
 | 
						|
                state.has_all({ItemNames.REAPER, ItemNames.REAPER_G4_CLUSTERBOMB}, self.player)
 | 
						|
                or state.has_all({ItemNames.SPECTRE, ItemNames.SPECTRE_PSIONIC_LASH}, self.player)
 | 
						|
                or state.has_any({ItemNames.VULTURE, ItemNames.LIBERATOR}, self.player)
 | 
						|
            )
 | 
						|
        )
 | 
						|
 | 
						|
    def terran_can_rescue(self, state) -> bool:
 | 
						|
        """
 | 
						|
        Rescuing in The Moebius Factor
 | 
						|
        :param state:
 | 
						|
        :return:
 | 
						|
        """
 | 
						|
        return state.has_any({ItemNames.MEDIVAC, ItemNames.HERCULES, ItemNames.RAVEN, ItemNames.VIKING}, self.player) or self.advanced_tactics
 | 
						|
 | 
						|
    def terran_beats_protoss_deathball(self, state: CollectionState) -> bool:
 | 
						|
        """
 | 
						|
        Ability to deal with Immortals, Colossi with some air support
 | 
						|
        :param state:
 | 
						|
        :return:
 | 
						|
        """
 | 
						|
        return (
 | 
						|
            (
 | 
						|
                state.has_any({ItemNames.BANSHEE, ItemNames.BATTLECRUISER}, self.player)
 | 
						|
                or state.has_all({ItemNames.LIBERATOR, ItemNames.LIBERATOR_RAID_ARTILLERY}, self.player)
 | 
						|
            ) and self.terran_competent_anti_air(state)
 | 
						|
            or self.terran_competent_comp(state) and self.terran_air_anti_air(state)
 | 
						|
        )
 | 
						|
 | 
						|
    def marine_medic_upgrade(self, state: CollectionState) -> bool:
 | 
						|
        """
 | 
						|
        Infantry upgrade to infantry-only no-build segments
 | 
						|
        :param state:
 | 
						|
        :return:
 | 
						|
        """
 | 
						|
        return state.has_any({
 | 
						|
            ItemNames.MARINE_COMBAT_SHIELD, ItemNames.MARINE_MAGRAIL_MUNITIONS, ItemNames.MEDIC_STABILIZER_MEDPACKS
 | 
						|
        }, self.player) \
 | 
						|
            or (state.count(ItemNames.MARINE_PROGRESSIVE_STIMPACK, self.player) >= 2
 | 
						|
                and state.has_group("Missions", self.player, 1))
 | 
						|
 | 
						|
    def terran_survives_rip_field(self, state: CollectionState) -> bool:
 | 
						|
        """
 | 
						|
        Ability to deal with large areas with environment damage
 | 
						|
        :param state:
 | 
						|
        :return:
 | 
						|
        """
 | 
						|
        return (state.has(ItemNames.BATTLECRUISER, self.player)
 | 
						|
                or self.terran_air(state) and self.terran_competent_anti_air(state) and self.terran_sustainable_mech_heal(state))
 | 
						|
 | 
						|
    def terran_sustainable_mech_heal(self, state: CollectionState) -> bool:
 | 
						|
        """
 | 
						|
        Can heal mech units without spending resources
 | 
						|
        :param state:
 | 
						|
        :return:
 | 
						|
        """
 | 
						|
        return state.has(ItemNames.SCIENCE_VESSEL, self.player) \
 | 
						|
            or state.has_all({ItemNames.MEDIC, ItemNames.MEDIC_ADAPTIVE_MEDPACKS}, self.player) \
 | 
						|
            or state.count(ItemNames.PROGRESSIVE_REGENERATIVE_BIO_STEEL, self.player) >= 3 \
 | 
						|
            or (self.advanced_tactics
 | 
						|
                and (
 | 
						|
                        state.has_all({ItemNames.RAVEN, ItemNames.RAVEN_BIO_MECHANICAL_REPAIR_DRONE}, self.player)
 | 
						|
                        or state.count(ItemNames.PROGRESSIVE_REGENERATIVE_BIO_STEEL, self.player) >= 2)
 | 
						|
                )
 | 
						|
 | 
						|
    def terran_bio_heal(self, state: CollectionState) -> bool:
 | 
						|
        """
 | 
						|
        Ability to heal bio units
 | 
						|
        :param state:
 | 
						|
        :return:
 | 
						|
        """
 | 
						|
        return state.has_any({ItemNames.MEDIC, ItemNames.MEDIVAC}, self.player) \
 | 
						|
            or self.advanced_tactics and state.has_all({ItemNames.RAVEN, ItemNames.RAVEN_BIO_MECHANICAL_REPAIR_DRONE}, self.player)
 | 
						|
 | 
						|
    def terran_base_trasher(self, state: CollectionState) -> bool:
 | 
						|
        """
 | 
						|
        Can attack heavily defended bases
 | 
						|
        :param state:
 | 
						|
        :return:
 | 
						|
        """
 | 
						|
        return state.has(ItemNames.SIEGE_TANK, self.player) \
 | 
						|
            or state.has_all({ItemNames.BATTLECRUISER, ItemNames.BATTLECRUISER_ATX_LASER_BATTERY}, self.player) \
 | 
						|
            or state.has_all({ItemNames.LIBERATOR, ItemNames.LIBERATOR_RAID_ARTILLERY}, self.player) \
 | 
						|
            or (self.advanced_tactics
 | 
						|
                and ((state.has_all({ItemNames.RAVEN, ItemNames.RAVEN_HUNTER_SEEKER_WEAPON}, self.player)
 | 
						|
                      or self.can_nuke(state))
 | 
						|
                     and (
 | 
						|
                             state.has_all({ItemNames.VIKING, ItemNames.VIKING_SHREDDER_ROUNDS}, self.player)
 | 
						|
                             or state.has_all({ItemNames.BANSHEE, ItemNames.BANSHEE_SHOCKWAVE_MISSILE_BATTERY}, self.player))
 | 
						|
                     )
 | 
						|
                )
 | 
						|
 | 
						|
    def terran_mobile_detector(self, state: CollectionState) -> bool:
 | 
						|
        return state.has_any({ItemNames.RAVEN, ItemNames.SCIENCE_VESSEL, ItemNames.PROGRESSIVE_ORBITAL_COMMAND}, self.player)
 | 
						|
 | 
						|
    def can_nuke(self, state: CollectionState) -> bool:
 | 
						|
        """
 | 
						|
        Ability to launch nukes
 | 
						|
        :param state:
 | 
						|
        :return:
 | 
						|
        """
 | 
						|
        return (self.advanced_tactics
 | 
						|
                and (state.has_any({ItemNames.GHOST, ItemNames.SPECTRE}, self.player)
 | 
						|
                     or state.has_all({ItemNames.THOR, ItemNames.THOR_BUTTON_WITH_A_SKULL_ON_IT}, self.player)))
 | 
						|
 | 
						|
    def terran_respond_to_colony_infestations(self, state: CollectionState) -> bool:
 | 
						|
        """
 | 
						|
        Can deal quickly with Brood Lords and Mutas in Haven's Fall and being able to progress the mission
 | 
						|
        :param state:
 | 
						|
        :return:
 | 
						|
        """
 | 
						|
        return (
 | 
						|
            self.terran_common_unit(state)
 | 
						|
            and self.terran_competent_anti_air(state)
 | 
						|
            and (
 | 
						|
                    self.terran_air_anti_air(state)
 | 
						|
                    or state.has_any({ItemNames.BATTLECRUISER, ItemNames.VALKYRIE}, self.player)
 | 
						|
                )
 | 
						|
            and self.terran_defense_rating(state, True) >= 3
 | 
						|
        )
 | 
						|
 | 
						|
    def engine_of_destruction_requirement(self, state: CollectionState):
 | 
						|
        return self.marine_medic_upgrade(state) \
 | 
						|
        and (
 | 
						|
                self.terran_competent_anti_air(state)
 | 
						|
                and self.terran_common_unit(state) or state.has(ItemNames.WRAITH, self.player)
 | 
						|
        )
 | 
						|
 | 
						|
    def all_in_requirement(self, state: CollectionState):
 | 
						|
        """
 | 
						|
        All-in
 | 
						|
        :param state:
 | 
						|
        :return:
 | 
						|
        """
 | 
						|
        beats_kerrigan = state.has_any({ItemNames.MARINE, ItemNames.BANSHEE, ItemNames.GHOST}, self.player) or self.advanced_tactics
 | 
						|
        if get_option_value(self.world, 'all_in_map') == AllInMap.option_ground:
 | 
						|
            # Ground
 | 
						|
            defense_rating = self.terran_defense_rating(state, True, False)
 | 
						|
            if state.has_any({ItemNames.BATTLECRUISER, ItemNames.BANSHEE}, self.player):
 | 
						|
                defense_rating += 2
 | 
						|
            return defense_rating >= 13 and beats_kerrigan
 | 
						|
        else:
 | 
						|
            # Air
 | 
						|
            defense_rating = self.terran_defense_rating(state, True, True)
 | 
						|
            return defense_rating >= 9 and beats_kerrigan \
 | 
						|
                and state.has_any({ItemNames.VIKING, ItemNames.BATTLECRUISER, ItemNames.VALKYRIE}, self.player) \
 | 
						|
                and state.has_any({ItemNames.HIVE_MIND_EMULATOR, ItemNames.PSI_DISRUPTER, ItemNames.MISSILE_TURRET}, self.player)
 | 
						|
 | 
						|
    # HotS
 | 
						|
    def zerg_common_unit(self, state: CollectionState) -> bool:
 | 
						|
        return state.has_any(self.basic_zerg_units, self.player)
 | 
						|
 | 
						|
    def zerg_competent_anti_air(self, state: CollectionState) -> bool:
 | 
						|
        return state.has_any({ItemNames.HYDRALISK, ItemNames.MUTALISK, ItemNames.CORRUPTOR, ItemNames.BROOD_QUEEN}, self.player) \
 | 
						|
            or state.has_all({ItemNames.SWARM_HOST, ItemNames.SWARM_HOST_PRESSURIZED_GLANDS}, self.player) \
 | 
						|
            or state.has_all({ItemNames.SCOURGE, ItemNames.SCOURGE_RESOURCE_EFFICIENCY}, self.player) \
 | 
						|
            or (self.advanced_tactics and state.has(ItemNames.INFESTOR, self.player))
 | 
						|
 | 
						|
    def zerg_basic_anti_air(self, state: CollectionState) -> bool:
 | 
						|
        return self.zerg_competent_anti_air(state) or self.kerrigan_unit_available in kerrigan_unit_available or \
 | 
						|
               state.has_any({ItemNames.SWARM_QUEEN, ItemNames.SCOURGE}, self.player) or (self.advanced_tactics and state.has(ItemNames.SPORE_CRAWLER, self.player))
 | 
						|
    
 | 
						|
    def morph_brood_lord(self, state: CollectionState) -> bool:
 | 
						|
        return state.has_any({ItemNames.MUTALISK, ItemNames.CORRUPTOR}, self.player) \
 | 
						|
            and state.has(ItemNames.MUTALISK_CORRUPTOR_BROOD_LORD_ASPECT, self.player)
 | 
						|
    
 | 
						|
    def morph_viper(self, state: CollectionState) -> bool:
 | 
						|
        return state.has_any({ItemNames.MUTALISK, ItemNames.CORRUPTOR}, self.player) \
 | 
						|
            and state.has(ItemNames.MUTALISK_CORRUPTOR_VIPER_ASPECT, self.player)
 | 
						|
 | 
						|
    def morph_impaler_or_lurker(self, state: CollectionState) -> bool:
 | 
						|
        return state.has(ItemNames.HYDRALISK, self.player) and state.has_any({ItemNames.HYDRALISK_IMPALER_ASPECT, ItemNames.HYDRALISK_LURKER_ASPECT}, self.player)
 | 
						|
 | 
						|
    def zerg_competent_comp(self, state: CollectionState) -> bool:
 | 
						|
        advanced = self.advanced_tactics
 | 
						|
        core_unit = state.has_any({ItemNames.ROACH, ItemNames.ABERRATION, ItemNames.ZERGLING}, self.player)
 | 
						|
        support_unit = state.has_any({ItemNames.SWARM_QUEEN, ItemNames.HYDRALISK}, self.player) \
 | 
						|
                       or self.morph_brood_lord(state) \
 | 
						|
                       or advanced and (state.has_any({ItemNames.INFESTOR, ItemNames.DEFILER}, self.player) or self.morph_viper(state))
 | 
						|
        if core_unit and support_unit:
 | 
						|
            return True
 | 
						|
        vespene_unit = state.has_any({ItemNames.ULTRALISK, ItemNames.ABERRATION}, self.player) \
 | 
						|
                       or advanced and self.morph_viper(state)
 | 
						|
        return vespene_unit and state.has_any({ItemNames.ZERGLING, ItemNames.SWARM_QUEEN}, self.player)
 | 
						|
 | 
						|
    def spread_creep(self, state: CollectionState) -> bool:
 | 
						|
        return self.advanced_tactics or state.has(ItemNames.SWARM_QUEEN, self.player)
 | 
						|
    
 | 
						|
    def zerg_competent_defense(self, state: CollectionState) -> bool:
 | 
						|
        return (
 | 
						|
            self.zerg_common_unit(state)
 | 
						|
            and (
 | 
						|
                (
 | 
						|
                    state.has(ItemNames.SWARM_HOST, self.player)
 | 
						|
                    or self.morph_brood_lord(state)
 | 
						|
                    or self.morph_impaler_or_lurker(state)
 | 
						|
                ) or (
 | 
						|
                    self.advanced_tactics
 | 
						|
                    and (self.morph_viper(state)
 | 
						|
                        or state.has(ItemNames.SPINE_CRAWLER, self.player))
 | 
						|
                )
 | 
						|
            )
 | 
						|
        )
 | 
						|
 | 
						|
    def basic_kerrigan(self, state: CollectionState) -> bool:
 | 
						|
        # One active ability that can be used to defeat enemies directly on Standard
 | 
						|
        if not self.advanced_tactics and \
 | 
						|
            not state.has_any({ItemNames.KERRIGAN_KINETIC_BLAST, ItemNames.KERRIGAN_LEAPING_STRIKE,
 | 
						|
                              ItemNames.KERRIGAN_CRUSHING_GRIP, ItemNames.KERRIGAN_PSIONIC_SHIFT,
 | 
						|
                              ItemNames.KERRIGAN_SPAWN_BANELINGS}, self.player):
 | 
						|
            return False
 | 
						|
        # Two non-ultimate abilities
 | 
						|
        count = 0
 | 
						|
        for item in (ItemNames.KERRIGAN_KINETIC_BLAST, ItemNames.KERRIGAN_LEAPING_STRIKE, ItemNames.KERRIGAN_HEROIC_FORTITUDE,
 | 
						|
                     ItemNames.KERRIGAN_CHAIN_REACTION, ItemNames.KERRIGAN_CRUSHING_GRIP, ItemNames.KERRIGAN_PSIONIC_SHIFT,
 | 
						|
                     ItemNames.KERRIGAN_SPAWN_BANELINGS, ItemNames.KERRIGAN_INFEST_BROODLINGS, ItemNames.KERRIGAN_FURY):
 | 
						|
            if state.has(item, self.player):
 | 
						|
                count += 1
 | 
						|
            if count >= 2:
 | 
						|
                return True
 | 
						|
        return False
 | 
						|
 | 
						|
    def two_kerrigan_actives(self, state: CollectionState) -> bool:
 | 
						|
        count = 0
 | 
						|
        for i in range(7):
 | 
						|
            if state.has_any(kerrigan_actives[i], self.player):
 | 
						|
                count += 1
 | 
						|
        return count >= 2
 | 
						|
 | 
						|
    def zerg_pass_vents(self, state: CollectionState) -> bool:
 | 
						|
        return self.story_tech_granted \
 | 
						|
            or state.has_any({ItemNames.ZERGLING, ItemNames.HYDRALISK, ItemNames.ROACH}, self.player) \
 | 
						|
            or (self.advanced_tactics and state.has(ItemNames.INFESTOR, self.player))
 | 
						|
 | 
						|
    def supreme_requirement(self, state: CollectionState) -> bool:
 | 
						|
        return self.story_tech_granted \
 | 
						|
            or not self.kerrigan_unit_available \
 | 
						|
            or (
 | 
						|
                state.has_all({ItemNames.KERRIGAN_LEAPING_STRIKE, ItemNames.KERRIGAN_MEND}, self.player)
 | 
						|
                and self.kerrigan_levels(state, 35)
 | 
						|
            )
 | 
						|
 | 
						|
    def kerrigan_levels(self, state: CollectionState, target: int) -> bool:
 | 
						|
        if self.story_levels_granted or not self.kerrigan_unit_available:
 | 
						|
            return True  # Levels are granted
 | 
						|
        if self.kerrigan_levels_per_mission_completed > 0 \
 | 
						|
           and self.kerrigan_levels_per_mission_completed_cap > 0 \
 | 
						|
           and not self.is_item_placement(state):
 | 
						|
            # Levels can be granted from mission completion.
 | 
						|
            # Item pool filtering isn't aware of missions beaten. Assume that missions beaten will fulfill this rule.
 | 
						|
            return True
 | 
						|
        # Levels from missions beaten
 | 
						|
        levels = self.kerrigan_levels_per_mission_completed * state.count_group("Missions", self.player)
 | 
						|
        if self.kerrigan_levels_per_mission_completed_cap != -1:
 | 
						|
            levels = min(levels, self.kerrigan_levels_per_mission_completed_cap)
 | 
						|
        # Levels from items
 | 
						|
        for kerrigan_level_item in kerrigan_levels:
 | 
						|
            level_amount = get_full_item_list()[kerrigan_level_item].number
 | 
						|
            item_count = state.count(kerrigan_level_item, self.player)
 | 
						|
            levels += item_count * level_amount
 | 
						|
        # Total level cap
 | 
						|
        if self.kerrigan_total_level_cap != -1:
 | 
						|
            levels = min(levels, self.kerrigan_total_level_cap)
 | 
						|
 | 
						|
        return levels >= target
 | 
						|
 | 
						|
 | 
						|
    def the_reckoning_requirement(self, state: CollectionState) -> bool:
 | 
						|
        if self.take_over_ai_allies:
 | 
						|
            return self.terran_competent_comp(state) \
 | 
						|
                and self.zerg_competent_comp(state) \
 | 
						|
                and (self.zerg_competent_anti_air(state)
 | 
						|
                     or self.terran_competent_anti_air(state))
 | 
						|
        else:
 | 
						|
            return self.zerg_competent_comp(state) \
 | 
						|
                and self.zerg_competent_anti_air(state)
 | 
						|
 | 
						|
    # LotV
 | 
						|
 | 
						|
    def protoss_common_unit(self, state: CollectionState) -> bool:
 | 
						|
        return state.has_any(self.basic_protoss_units, self.player)
 | 
						|
 | 
						|
    def protoss_basic_anti_air(self, state: CollectionState) -> bool:
 | 
						|
        return self.protoss_competent_anti_air(state) \
 | 
						|
            or state.has_any({ItemNames.PHOENIX, ItemNames.MIRAGE, ItemNames.CORSAIR, ItemNames.CARRIER, ItemNames.SCOUT,
 | 
						|
                             ItemNames.DARK_ARCHON, ItemNames.WRATHWALKER, ItemNames.MOTHERSHIP}, self.player) \
 | 
						|
            or state.has_all({ItemNames.WARP_PRISM, ItemNames.WARP_PRISM_PHASE_BLASTER}, self.player) \
 | 
						|
            or self.advanced_tactics and state.has_any(
 | 
						|
                {ItemNames.HIGH_TEMPLAR, ItemNames.SIGNIFIER, ItemNames.ASCENDANT, ItemNames.DARK_TEMPLAR,
 | 
						|
                 ItemNames.SENTRY, ItemNames.ENERGIZER}, self.player)
 | 
						|
 | 
						|
    def protoss_anti_armor_anti_air(self, state: CollectionState) -> bool:
 | 
						|
        return self.protoss_competent_anti_air(state) \
 | 
						|
            or state.has_any({ItemNames.SCOUT, ItemNames.WRATHWALKER}, self.player) \
 | 
						|
            or (state.has_any({ItemNames.IMMORTAL, ItemNames.ANNIHILATOR}, self.player)
 | 
						|
                and state.has(ItemNames.IMMORTAL_ANNIHILATOR_ADVANCED_TARGETING_MECHANICS, self.player))
 | 
						|
 | 
						|
    def protoss_anti_light_anti_air(self, state: CollectionState) -> bool:
 | 
						|
        return self.protoss_competent_anti_air(state) \
 | 
						|
            or state.has_any({ItemNames.PHOENIX, ItemNames.MIRAGE, ItemNames.CORSAIR, ItemNames.CARRIER}, self.player)
 | 
						|
 | 
						|
    def protoss_competent_anti_air(self, state: CollectionState) -> bool:
 | 
						|
        return state.has_any(
 | 
						|
            {ItemNames.STALKER, ItemNames.SLAYER, ItemNames.INSTIGATOR, ItemNames.DRAGOON, ItemNames.ADEPT,
 | 
						|
             ItemNames.VOID_RAY, ItemNames.DESTROYER, ItemNames.TEMPEST}, self.player) \
 | 
						|
            or (state.has_any({ItemNames.PHOENIX, ItemNames.MIRAGE, ItemNames.CORSAIR, ItemNames.CARRIER}, self.player)
 | 
						|
                and state.has_any({ItemNames.SCOUT, ItemNames.WRATHWALKER}, self.player)) \
 | 
						|
            or (self.advanced_tactics
 | 
						|
                and state.has_any({ItemNames.IMMORTAL, ItemNames.ANNIHILATOR}, self.player)
 | 
						|
                and state.has(ItemNames.IMMORTAL_ANNIHILATOR_ADVANCED_TARGETING_MECHANICS, self.player))
 | 
						|
 | 
						|
    def protoss_has_blink(self, state: CollectionState) -> bool:
 | 
						|
        return state.has_any({ItemNames.STALKER, ItemNames.INSTIGATOR, ItemNames.SLAYER}, self.player) \
 | 
						|
            or (
 | 
						|
                    state.has(ItemNames.DARK_TEMPLAR_AVENGER_BLOOD_HUNTER_BLINK, self.player)
 | 
						|
                    and state.has_any({ItemNames.DARK_TEMPLAR, ItemNames.BLOOD_HUNTER, ItemNames.AVENGER}, self.player)
 | 
						|
            )
 | 
						|
 | 
						|
    def protoss_can_attack_behind_chasm(self, state: CollectionState) -> bool:
 | 
						|
        return state.has_any(
 | 
						|
            {ItemNames.SCOUT, ItemNames.TEMPEST,
 | 
						|
             ItemNames.CARRIER, ItemNames.VOID_RAY, ItemNames.DESTROYER, ItemNames.MOTHERSHIP}, self.player) \
 | 
						|
            or self.protoss_has_blink(state) \
 | 
						|
            or (state.has(ItemNames.WARP_PRISM, self.player)
 | 
						|
                and (self.protoss_common_unit(state) or state.has(ItemNames.WARP_PRISM_PHASE_BLASTER, self.player))) \
 | 
						|
            or (self.advanced_tactics
 | 
						|
                and state.has_any({ItemNames.ORACLE, ItemNames.ARBITER}, self.player))
 | 
						|
 | 
						|
    def protoss_fleet(self, state: CollectionState) -> bool:
 | 
						|
        return state.has_any({ItemNames.CARRIER, ItemNames.TEMPEST, ItemNames.VOID_RAY, ItemNames.DESTROYER}, self.player)
 | 
						|
 | 
						|
    def templars_return_requirement(self, state: CollectionState) -> bool:
 | 
						|
        return self.story_tech_granted \
 | 
						|
            or (
 | 
						|
                state.has_any({ItemNames.IMMORTAL, ItemNames.ANNIHILATOR}, self.player)
 | 
						|
                and state.has_any({ItemNames.COLOSSUS, ItemNames.VANGUARD, ItemNames.REAVER, ItemNames.DARK_TEMPLAR}, self.player)
 | 
						|
                and state.has_any({ItemNames.SENTRY, ItemNames.HIGH_TEMPLAR}, self.player)
 | 
						|
            )
 | 
						|
 | 
						|
    def brothers_in_arms_requirement(self, state: CollectionState) -> bool:
 | 
						|
        return (
 | 
						|
                self.protoss_common_unit(state)
 | 
						|
                and self.protoss_anti_armor_anti_air(state)
 | 
						|
                and self.protoss_hybrid_counter(state)
 | 
						|
        ) or (
 | 
						|
                self.take_over_ai_allies
 | 
						|
                and (
 | 
						|
                        self.terran_common_unit(state)
 | 
						|
                        or self.protoss_common_unit(state)
 | 
						|
                )
 | 
						|
                and (
 | 
						|
                        self.terran_competent_anti_air(state)
 | 
						|
                        or self.protoss_anti_armor_anti_air(state)
 | 
						|
                )
 | 
						|
                and (
 | 
						|
                        self.protoss_hybrid_counter(state)
 | 
						|
                        or state.has_any({ItemNames.BATTLECRUISER, ItemNames.LIBERATOR, ItemNames.SIEGE_TANK}, self.player)
 | 
						|
                        or state.has_all({ItemNames.SPECTRE, ItemNames.SPECTRE_PSIONIC_LASH}, self.player)
 | 
						|
                        or (state.has(ItemNames.IMMORTAL, self.player)
 | 
						|
                            and state.has_any({ItemNames.MARINE, ItemNames.MARAUDER}, self.player)
 | 
						|
                            and self.terran_bio_heal(state))
 | 
						|
                )
 | 
						|
        )
 | 
						|
 | 
						|
    def protoss_hybrid_counter(self, state: CollectionState) -> bool:
 | 
						|
        """
 | 
						|
        Ground Hybrids
 | 
						|
        """
 | 
						|
        return state.has_any(
 | 
						|
            {ItemNames.ANNIHILATOR, ItemNames.ASCENDANT, ItemNames.TEMPEST, ItemNames.CARRIER, ItemNames.VOID_RAY,
 | 
						|
             ItemNames.WRATHWALKER, ItemNames.VANGUARD}, self.player) \
 | 
						|
            or (state.has(ItemNames.IMMORTAL, self.player) or self.advanced_tactics) and state.has_any(
 | 
						|
                {ItemNames.STALKER, ItemNames.DRAGOON, ItemNames.ADEPT, ItemNames.INSTIGATOR, ItemNames.SLAYER}, self.player)
 | 
						|
 | 
						|
    def the_infinite_cycle_requirement(self, state: CollectionState) -> bool:
 | 
						|
        return self.story_tech_granted \
 | 
						|
            or not self.kerrigan_unit_available \
 | 
						|
            or (
 | 
						|
                self.two_kerrigan_actives(state)
 | 
						|
                and self.basic_kerrigan(state)
 | 
						|
                and self.kerrigan_levels(state, 70)
 | 
						|
            )
 | 
						|
 | 
						|
    def protoss_basic_splash(self, state: CollectionState) -> bool:
 | 
						|
        return state.has_any(
 | 
						|
            {ItemNames.ZEALOT, ItemNames.COLOSSUS, ItemNames.VANGUARD, ItemNames.HIGH_TEMPLAR, ItemNames.SIGNIFIER,
 | 
						|
             ItemNames.DARK_TEMPLAR, ItemNames.REAVER, ItemNames.ASCENDANT}, self.player)
 | 
						|
 | 
						|
    def protoss_static_defense(self, state: CollectionState) -> bool:
 | 
						|
        return state.has_any({ItemNames.PHOTON_CANNON, ItemNames.KHAYDARIN_MONOLITH}, self.player)
 | 
						|
 | 
						|
    def last_stand_requirement(self, state: CollectionState) -> bool:
 | 
						|
        return self.protoss_common_unit(state) \
 | 
						|
            and self.protoss_competent_anti_air(state) \
 | 
						|
            and self.protoss_static_defense(state) \
 | 
						|
            and (
 | 
						|
                self.advanced_tactics
 | 
						|
                or self.protoss_basic_splash(state)
 | 
						|
            )
 | 
						|
 | 
						|
    def harbinger_of_oblivion_requirement(self, state: CollectionState) -> bool:
 | 
						|
        return self.protoss_anti_armor_anti_air(state) and (
 | 
						|
                self.take_over_ai_allies
 | 
						|
                or (
 | 
						|
                        self.protoss_common_unit(state)
 | 
						|
                        and self.protoss_hybrid_counter(state)
 | 
						|
                )
 | 
						|
        )
 | 
						|
 | 
						|
    def protoss_competent_comp(self, state: CollectionState) -> bool:
 | 
						|
        return self.protoss_common_unit(state) \
 | 
						|
            and self.protoss_competent_anti_air(state) \
 | 
						|
            and self.protoss_hybrid_counter(state) \
 | 
						|
            and self.protoss_basic_splash(state)
 | 
						|
 | 
						|
    def protoss_stalker_upgrade(self, state: CollectionState) -> bool:
 | 
						|
        return (
 | 
						|
                state.has_any(
 | 
						|
                    {
 | 
						|
                        ItemNames.STALKER_INSTIGATOR_SLAYER_DISINTEGRATING_PARTICLES,
 | 
						|
                        ItemNames.STALKER_INSTIGATOR_SLAYER_PARTICLE_REFLECTION
 | 
						|
                    }, self.player)
 | 
						|
                and self.lock_any_item(state, {ItemNames.STALKER, ItemNames.INSTIGATOR, ItemNames.SLAYER})
 | 
						|
        )
 | 
						|
 | 
						|
    def steps_of_the_rite_requirement(self, state: CollectionState) -> bool:
 | 
						|
        return self.protoss_competent_comp(state) \
 | 
						|
            or (
 | 
						|
                    self.protoss_common_unit(state)
 | 
						|
                    and self.protoss_competent_anti_air(state)
 | 
						|
                    and self.protoss_static_defense(state)
 | 
						|
            )
 | 
						|
 | 
						|
    def protoss_heal(self, state: CollectionState) -> bool:
 | 
						|
        return state.has_any({ItemNames.CARRIER, ItemNames.SENTRY, ItemNames.SHIELD_BATTERY, ItemNames.RECONSTRUCTION_BEAM}, self.player)
 | 
						|
 | 
						|
    def templars_charge_requirement(self, state: CollectionState) -> bool:
 | 
						|
        return self.protoss_heal(state) \
 | 
						|
            and self.protoss_anti_armor_anti_air(state) \
 | 
						|
            and (
 | 
						|
                    self.protoss_fleet(state)
 | 
						|
                    or (self.advanced_tactics
 | 
						|
                        and self.protoss_competent_comp(state)
 | 
						|
                        )
 | 
						|
            )
 | 
						|
 | 
						|
    def the_host_requirement(self, state: CollectionState) -> bool:
 | 
						|
        return (self.protoss_fleet(state)
 | 
						|
                and self.protoss_static_defense(state)
 | 
						|
                ) or (
 | 
						|
                self.protoss_competent_comp(state)
 | 
						|
                and state.has(ItemNames.SOA_TIME_STOP, self.player)
 | 
						|
        )
 | 
						|
 | 
						|
    def salvation_requirement(self, state: CollectionState) -> bool:
 | 
						|
        return [
 | 
						|
            self.protoss_competent_comp(state),
 | 
						|
            self.protoss_fleet(state),
 | 
						|
            self.protoss_static_defense(state)
 | 
						|
        ].count(True) >= 2
 | 
						|
 | 
						|
    def into_the_void_requirement(self, state: CollectionState) -> bool:
 | 
						|
        return self.protoss_competent_comp(state) \
 | 
						|
            or (
 | 
						|
                    self.take_over_ai_allies
 | 
						|
                    and (
 | 
						|
                            state.has(ItemNames.BATTLECRUISER, self.player)
 | 
						|
                            or (
 | 
						|
                                    state.has(ItemNames.ULTRALISK, self.player)
 | 
						|
                                    and self.protoss_competent_anti_air(state)
 | 
						|
                            )
 | 
						|
                    )
 | 
						|
            )
 | 
						|
 | 
						|
    def essence_of_eternity_requirement(self, state: CollectionState) -> bool:
 | 
						|
        defense_score = self.terran_defense_rating(state, False, True)
 | 
						|
        if self.take_over_ai_allies and self.protoss_static_defense(state):
 | 
						|
            defense_score += 2
 | 
						|
        return defense_score >= 10 \
 | 
						|
            and (
 | 
						|
                    self.terran_competent_anti_air(state)
 | 
						|
                    or self.take_over_ai_allies
 | 
						|
                    and self.protoss_competent_anti_air(state)
 | 
						|
            ) \
 | 
						|
            and (
 | 
						|
                    state.has(ItemNames.BATTLECRUISER, self.player)
 | 
						|
                    or (state.has(ItemNames.BANSHEE, self.player) and state.has_any({ItemNames.VIKING, ItemNames.VALKYRIE},
 | 
						|
                                                                                    self.player))
 | 
						|
                    or self.take_over_ai_allies and self.protoss_fleet(state)
 | 
						|
            ) \
 | 
						|
            and state.has_any({ItemNames.SIEGE_TANK, ItemNames.LIBERATOR}, self.player)
 | 
						|
 | 
						|
    def amons_fall_requirement(self, state: CollectionState) -> bool:
 | 
						|
        if self.take_over_ai_allies:
 | 
						|
            return (
 | 
						|
                    (
 | 
						|
                        state.has_any({ItemNames.BATTLECRUISER, ItemNames.CARRIER}, self.player)
 | 
						|
                    )
 | 
						|
                    or (state.has(ItemNames.ULTRALISK, self.player)
 | 
						|
                        and self.protoss_competent_anti_air(state)
 | 
						|
                        and (
 | 
						|
                                state.has_any({ItemNames.LIBERATOR, ItemNames.BANSHEE, ItemNames.VALKYRIE, ItemNames.VIKING}, self.player)
 | 
						|
                                or state.has_all({ItemNames.WRAITH, ItemNames.WRAITH_ADVANCED_LASER_TECHNOLOGY}, self.player)
 | 
						|
                                or self.protoss_fleet(state)
 | 
						|
                        )
 | 
						|
                        and (self.terran_sustainable_mech_heal(state)
 | 
						|
                             or (self.spear_of_adun_autonomously_cast_presence == SpearOfAdunAutonomouslyCastAbilityPresence.option_everywhere
 | 
						|
                                 and state.has(ItemNames.RECONSTRUCTION_BEAM, self.player))
 | 
						|
                             )
 | 
						|
                        )
 | 
						|
            ) \
 | 
						|
                and self.terran_competent_anti_air(state) \
 | 
						|
                and self.protoss_competent_comp(state) \
 | 
						|
                and self.zerg_competent_comp(state)
 | 
						|
        else:
 | 
						|
            return state.has(ItemNames.MUTALISK, self.player) and self.zerg_competent_comp(state)
 | 
						|
 | 
						|
    def nova_any_weapon(self, state: CollectionState) -> bool:
 | 
						|
        return state.has_any(
 | 
						|
            {ItemNames.NOVA_C20A_CANISTER_RIFLE, ItemNames.NOVA_HELLFIRE_SHOTGUN, ItemNames.NOVA_PLASMA_RIFLE,
 | 
						|
             ItemNames.NOVA_MONOMOLECULAR_BLADE, ItemNames.NOVA_BLAZEFIRE_GUNBLADE}, self.player)
 | 
						|
 | 
						|
    def nova_ranged_weapon(self, state: CollectionState) -> bool:
 | 
						|
        return state.has_any(
 | 
						|
            {ItemNames.NOVA_C20A_CANISTER_RIFLE, ItemNames.NOVA_HELLFIRE_SHOTGUN, ItemNames.NOVA_PLASMA_RIFLE},
 | 
						|
            self.player)
 | 
						|
 | 
						|
    def nova_splash(self, state: CollectionState) -> bool:
 | 
						|
        return state.has_any({
 | 
						|
            ItemNames.NOVA_HELLFIRE_SHOTGUN, ItemNames.NOVA_BLAZEFIRE_GUNBLADE, ItemNames.NOVA_PULSE_GRENADES
 | 
						|
        }, self.player) \
 | 
						|
            or self.advanced_tactics and state.has_any(
 | 
						|
                {ItemNames.NOVA_PLASMA_RIFLE, ItemNames.NOVA_MONOMOLECULAR_BLADE}, self.player)
 | 
						|
 | 
						|
    def nova_dash(self, state: CollectionState) -> bool:
 | 
						|
        return state.has_any({ItemNames.NOVA_MONOMOLECULAR_BLADE, ItemNames.NOVA_BLINK}, self.player)
 | 
						|
 | 
						|
    def nova_full_stealth(self, state: CollectionState) -> bool:
 | 
						|
        return state.count(ItemNames.NOVA_PROGRESSIVE_STEALTH_SUIT_MODULE, self.player) >= 2
 | 
						|
 | 
						|
    def nova_heal(self, state: CollectionState) -> bool:
 | 
						|
        return state.has_any({ItemNames.NOVA_ARMORED_SUIT_MODULE, ItemNames.NOVA_STIM_INFUSION}, self.player)
 | 
						|
 | 
						|
    def nova_escape_assist(self, state: CollectionState) -> bool:
 | 
						|
        return state.has_any({ItemNames.NOVA_BLINK, ItemNames.NOVA_HOLO_DECOY, ItemNames.NOVA_IONIC_FORCE_FIELD}, self.player)
 | 
						|
 | 
						|
    def the_escape_stuff_granted(self) -> bool:
 | 
						|
        """
 | 
						|
        The NCO first mission requires having too much stuff first before actually able to do anything
 | 
						|
        :return:
 | 
						|
        """
 | 
						|
        return self.story_tech_granted \
 | 
						|
            or (self.mission_order == MissionOrder.option_vanilla and self.enabled_campaigns == {SC2Campaign.NCO})
 | 
						|
 | 
						|
    def the_escape_first_stage_requirement(self, state: CollectionState) -> bool:
 | 
						|
        return self.the_escape_stuff_granted() \
 | 
						|
            or (self.nova_ranged_weapon(state) and (self.nova_full_stealth(state) or self.nova_heal(state)))
 | 
						|
 | 
						|
    def the_escape_requirement(self, state: CollectionState) -> bool:
 | 
						|
        return self.the_escape_first_stage_requirement(state) \
 | 
						|
            and (self.the_escape_stuff_granted() or self.nova_splash(state))
 | 
						|
 | 
						|
    def terran_cliffjumper(self, state: CollectionState) -> bool:
 | 
						|
        return state.has(ItemNames.REAPER, self.player) \
 | 
						|
                or state.has_all({ItemNames.GOLIATH, ItemNames.GOLIATH_JUMP_JETS}, self.player) \
 | 
						|
                or state.has_all({ItemNames.SIEGE_TANK, ItemNames.SIEGE_TANK_JUMP_JETS}, self.player)
 | 
						|
 | 
						|
    def terran_able_to_snipe_defiler(self, state: CollectionState) -> bool:
 | 
						|
        return state.has_all({ItemNames.NOVA_JUMP_SUIT_MODULE, ItemNames.NOVA_C20A_CANISTER_RIFLE}, self.player) \
 | 
						|
                or state.has_all({ItemNames.SIEGE_TANK, ItemNames.SIEGE_TANK_MAELSTROM_ROUNDS, ItemNames.SIEGE_TANK_JUMP_JETS}, self.player)
 | 
						|
 | 
						|
    def sudden_strike_requirement(self, state: CollectionState) -> bool:
 | 
						|
        return self.sudden_strike_can_reach_objectives(state) \
 | 
						|
            and self.terran_able_to_snipe_defiler(state) \
 | 
						|
            and state.has_any({ItemNames.SIEGE_TANK, ItemNames.VULTURE}, self.player) \
 | 
						|
            and self.nova_splash(state) \
 | 
						|
            and (self.terran_defense_rating(state, True, False) >= 2
 | 
						|
                 or state.has(ItemNames.NOVA_JUMP_SUIT_MODULE, self.player))
 | 
						|
 | 
						|
    def sudden_strike_can_reach_objectives(self, state: CollectionState) -> bool:
 | 
						|
        return self.terran_cliffjumper(state) \
 | 
						|
            or state.has_any({ItemNames.BANSHEE, ItemNames.VIKING}, self.player) \
 | 
						|
            or (
 | 
						|
                    self.advanced_tactics
 | 
						|
                    and state.has(ItemNames.MEDIVAC, self.player)
 | 
						|
                    and state.has_any({ItemNames.MARINE, ItemNames.MARAUDER, ItemNames.VULTURE, ItemNames.HELLION,
 | 
						|
                                       ItemNames.GOLIATH}, self.player)
 | 
						|
            )
 | 
						|
 | 
						|
    def enemy_intelligence_garrisonable_unit(self, state: CollectionState) -> bool:
 | 
						|
        """
 | 
						|
        Has unit usable as a Garrison in Enemy Intelligence
 | 
						|
        :param state:
 | 
						|
        :return:
 | 
						|
        """
 | 
						|
        return state.has_any(
 | 
						|
            {ItemNames.MARINE, ItemNames.REAPER, ItemNames.MARAUDER, ItemNames.GHOST, ItemNames.SPECTRE,
 | 
						|
             ItemNames.HELLION, ItemNames.GOLIATH, ItemNames.WARHOUND, ItemNames.DIAMONDBACK, ItemNames.VIKING},
 | 
						|
            self.player)
 | 
						|
 | 
						|
    def enemy_intelligence_cliff_garrison(self, state: CollectionState) -> bool:
 | 
						|
        return state.has_any({ItemNames.REAPER, ItemNames.VIKING, ItemNames.MEDIVAC, ItemNames.HERCULES}, self.player) \
 | 
						|
            or state.has_all({ItemNames.GOLIATH, ItemNames.GOLIATH_JUMP_JETS}, self.player) \
 | 
						|
            or self.advanced_tactics and state.has_any({ItemNames.HELS_ANGELS, ItemNames.BRYNHILDS}, self.player)
 | 
						|
 | 
						|
    def enemy_intelligence_first_stage_requirement(self, state: CollectionState) -> bool:
 | 
						|
        return self.enemy_intelligence_garrisonable_unit(state) \
 | 
						|
            and (self.terran_competent_comp(state)
 | 
						|
                 or (
 | 
						|
                         self.terran_common_unit(state)
 | 
						|
                         and self.terran_competent_anti_air(state)
 | 
						|
                         and state.has(ItemNames.NOVA_NUKE, self.player)
 | 
						|
                 )
 | 
						|
                 ) \
 | 
						|
            and self.terran_defense_rating(state, True, True) >= 5
 | 
						|
 | 
						|
    def enemy_intelligence_second_stage_requirement(self, state: CollectionState) -> bool:
 | 
						|
        return self.enemy_intelligence_first_stage_requirement(state) \
 | 
						|
            and self.enemy_intelligence_cliff_garrison(state) \
 | 
						|
            and (
 | 
						|
                    self.story_tech_granted
 | 
						|
                    or (
 | 
						|
                            self.nova_any_weapon(state)
 | 
						|
                            and (
 | 
						|
                                    self.nova_full_stealth(state)
 | 
						|
                                    or (self.nova_heal(state)
 | 
						|
                                        and self.nova_splash(state)
 | 
						|
                                        and self.nova_ranged_weapon(state))
 | 
						|
                            )
 | 
						|
                    )
 | 
						|
            )
 | 
						|
 | 
						|
    def enemy_intelligence_third_stage_requirement(self, state: CollectionState) -> bool:
 | 
						|
        return self.enemy_intelligence_second_stage_requirement(state) \
 | 
						|
            and (
 | 
						|
                    self.story_tech_granted
 | 
						|
                    or (
 | 
						|
                            state.has(ItemNames.NOVA_PROGRESSIVE_STEALTH_SUIT_MODULE, self.player)
 | 
						|
                            and self.nova_dash(state)
 | 
						|
                    )
 | 
						|
            )
 | 
						|
 | 
						|
    def trouble_in_paradise_requirement(self, state: CollectionState) -> bool:
 | 
						|
        return self.nova_any_weapon(state) \
 | 
						|
            and self.nova_splash(state) \
 | 
						|
            and self.terran_beats_protoss_deathball(state) \
 | 
						|
            and self.terran_defense_rating(state, True, True) >= 7
 | 
						|
 | 
						|
    def night_terrors_requirement(self, state: CollectionState) -> bool:
 | 
						|
        return self.terran_common_unit(state) \
 | 
						|
            and self.terran_competent_anti_air(state) \
 | 
						|
            and (
 | 
						|
                # These can handle the waves of infested, even volatile ones
 | 
						|
                    state.has(ItemNames.SIEGE_TANK, self.player)
 | 
						|
                    or state.has_all({ItemNames.VIKING, ItemNames.VIKING_SHREDDER_ROUNDS}, self.player)
 | 
						|
                    or (
 | 
						|
                            (
 | 
						|
                                # Regular infesteds
 | 
						|
                                    state.has(ItemNames.FIREBAT, self.player)
 | 
						|
                                    or state.has_all({ItemNames.HELLION, ItemNames.HELLION_HELLBAT_ASPECT}, self.player)
 | 
						|
                                    or (
 | 
						|
                                            self.advanced_tactics
 | 
						|
                                            and state.has_any({ItemNames.PERDITION_TURRET, ItemNames.PLANETARY_FORTRESS}, self.player)
 | 
						|
                                    )
 | 
						|
                            )
 | 
						|
                            and self.terran_bio_heal(state)
 | 
						|
                            and (
 | 
						|
                                # Volatile infesteds
 | 
						|
                                    state.has(ItemNames.LIBERATOR, self.player)
 | 
						|
                                    or (
 | 
						|
                                            self.advanced_tactics
 | 
						|
                                            and state.has_any({ItemNames.HERC, ItemNames.VULTURE}, self.player)
 | 
						|
                                    )
 | 
						|
                            )
 | 
						|
                    )
 | 
						|
            )
 | 
						|
 | 
						|
    def flashpoint_far_requirement(self, state: CollectionState) -> bool:
 | 
						|
        return self.terran_competent_comp(state) \
 | 
						|
            and self.terran_mobile_detector(state) \
 | 
						|
            and self.terran_defense_rating(state, True, False) >= 6
 | 
						|
 | 
						|
    def enemy_shadow_tripwires_tool(self, state: CollectionState) -> bool:
 | 
						|
        return state.has_any({ItemNames.NOVA_FLASHBANG_GRENADES, ItemNames.NOVA_BLINK, ItemNames.NOVA_DOMINATION},
 | 
						|
                             self.player)
 | 
						|
 | 
						|
    def enemy_shadow_door_unlocks_tool(self, state: CollectionState) -> bool:
 | 
						|
        return state.has_any({ItemNames.NOVA_DOMINATION, ItemNames.NOVA_BLINK, ItemNames.NOVA_JUMP_SUIT_MODULE},
 | 
						|
                             self.player)
 | 
						|
 | 
						|
    def enemy_shadow_domination(self, state: CollectionState) -> bool:
 | 
						|
        return self.story_tech_granted \
 | 
						|
            or (self.nova_ranged_weapon(state)
 | 
						|
                and (self.nova_full_stealth(state)
 | 
						|
                     or state.has(ItemNames.NOVA_JUMP_SUIT_MODULE, self.player)
 | 
						|
                     or (self.nova_heal(state) and self.nova_splash(state))
 | 
						|
                     )
 | 
						|
                )
 | 
						|
 | 
						|
    def enemy_shadow_first_stage(self, state: CollectionState) -> bool:
 | 
						|
        return self.enemy_shadow_domination(state) \
 | 
						|
            and (self.story_tech_granted
 | 
						|
                 or ((self.nova_full_stealth(state) and self.enemy_shadow_tripwires_tool(state))
 | 
						|
                     or (self.nova_heal(state) and self.nova_splash(state))
 | 
						|
                     )
 | 
						|
                 )
 | 
						|
 | 
						|
    def enemy_shadow_second_stage(self, state: CollectionState) -> bool:
 | 
						|
        return self.enemy_shadow_first_stage(state) \
 | 
						|
            and (self.story_tech_granted
 | 
						|
                 or self.nova_splash(state)
 | 
						|
                 or self.nova_heal(state)
 | 
						|
                 or self.nova_escape_assist(state)
 | 
						|
                 )
 | 
						|
 | 
						|
    def enemy_shadow_door_controls(self, state: CollectionState) -> bool:
 | 
						|
        return self.enemy_shadow_second_stage(state) \
 | 
						|
            and (self.story_tech_granted or self.enemy_shadow_door_unlocks_tool(state))
 | 
						|
 | 
						|
    def enemy_shadow_victory(self, state: CollectionState) -> bool:
 | 
						|
        return self.enemy_shadow_door_controls(state) \
 | 
						|
            and (self.story_tech_granted or self.nova_heal(state))
 | 
						|
 | 
						|
    def dark_skies_requirement(self, state: CollectionState) -> bool:
 | 
						|
        return self.terran_common_unit(state) \
 | 
						|
            and self.terran_beats_protoss_deathball(state) \
 | 
						|
            and self.terran_defense_rating(state, False, True) >= 8
 | 
						|
 | 
						|
    def end_game_requirement(self, state: CollectionState) -> bool:
 | 
						|
        return self.terran_competent_comp(state) \
 | 
						|
            and self.terran_mobile_detector(state) \
 | 
						|
            and (
 | 
						|
                    state.has_any({ItemNames.BATTLECRUISER, ItemNames.LIBERATOR, ItemNames.BANSHEE}, self.player)
 | 
						|
                    or state.has_all({ItemNames.WRAITH, ItemNames.WRAITH_ADVANCED_LASER_TECHNOLOGY}, self.player)
 | 
						|
            ) \
 | 
						|
            and (state.has_any({ItemNames.BATTLECRUISER, ItemNames.VIKING, ItemNames.LIBERATOR}, self.player)
 | 
						|
                 or (self.advanced_tactics
 | 
						|
                     and state.has_all({ItemNames.RAVEN, ItemNames.RAVEN_HUNTER_SEEKER_WEAPON}, self.player)
 | 
						|
                     )
 | 
						|
                 )
 | 
						|
 | 
						|
    def __init__(self, world: World):
 | 
						|
        self.world: World = world
 | 
						|
        self.player = None if world is None else world.player
 | 
						|
        self.logic_level = get_option_value(world, 'required_tactics')
 | 
						|
        self.advanced_tactics = self.logic_level != RequiredTactics.option_standard
 | 
						|
        self.take_over_ai_allies = get_option_value(world, "take_over_ai_allies") == TakeOverAIAllies.option_true
 | 
						|
        self.kerrigan_unit_available = get_option_value(world, 'kerrigan_presence') in kerrigan_unit_available \
 | 
						|
            and SC2Campaign.HOTS in get_enabled_campaigns(world)
 | 
						|
        self.kerrigan_levels_per_mission_completed = get_option_value(world, "kerrigan_levels_per_mission_completed")
 | 
						|
        self.kerrigan_levels_per_mission_completed_cap = get_option_value(world, "kerrigan_levels_per_mission_completed_cap")
 | 
						|
        self.kerrigan_total_level_cap = get_option_value(world, "kerrigan_total_level_cap")
 | 
						|
        self.story_tech_granted = get_option_value(world, "grant_story_tech") == GrantStoryTech.option_true
 | 
						|
        self.story_levels_granted = get_option_value(world, "grant_story_levels") != GrantStoryLevels.option_disabled
 | 
						|
        self.basic_terran_units = get_basic_units(world, SC2Race.TERRAN)
 | 
						|
        self.basic_zerg_units = get_basic_units(world, SC2Race.ZERG)
 | 
						|
        self.basic_protoss_units = get_basic_units(world, SC2Race.PROTOSS)
 | 
						|
        self.spear_of_adun_autonomously_cast_presence = get_option_value(world, "spear_of_adun_autonomously_cast_ability_presence")
 | 
						|
        self.enabled_campaigns = get_enabled_campaigns(world)
 | 
						|
        self.mission_order = get_option_value(world, "mission_order")
 |