from typing import TYPE_CHECKING, Callable, Set, ClassVar from dataclasses import dataclass, field from warnings import warn import math from BaseClasses import CollectionState from Options import OptionError from .Locations import CAMERAS_INDEX, CAMERAS_MASTER, CELLPHONES_INDEX, Cellphone_Name_to_ID, MONKEYS_BOSSES, \ MONKEYS_BREAK_ROOMS, MONKEYS_INDEX, MONKEYS_MASTER, MONKEYS_PASSWORDS, generate_name_to_id, LOCATIONS_INDEX, \ LOCATIONS_DIRECTORY, SHOP_CHEAP_COLLECTION_INDEX, SHOP_CHEAP_MASTER, SHOP_PROGRESSION_DIRECTORY, SHOP_UNIQUE_MASTER, \ SHOP_COLLECTION_MASTER, SHOP_PERSISTENT_MASTER, SHOP_CHEAP_COLLECTION_MASTER, SHOP_EVENT_ACCESS_DIRECTORY, \ MONKEYS_INFINITE_GADGET_FLOAT_APPLICABLE, EVENTS_INFINITE_GADGET_FLOAT_APPLICABLE from .Logic import Rulesets, AccessRule, has_keys, event_invoked, has_enough_keys, can_access_region, \ has_shop_stock, has_morph_stocks, has_morph_extensions, can_farm_boxes, can_farm_sneaky_borgs from .Strings import Loc, Stage, Events, APHelper from .Stages import (STAGES_DIRECTORY, STAGES_DIRECTORY_LABEL, ENTRANCES_SHOP_PSEUDOREGIONS, AE3EntranceMeta, ENTRANCES_SHOP_PROGRESSION, STAGES_SHOP_PROGRESSION, STAGES_FARMABLE, STAGES_FARMABLE_SNEAKY_BORG, LEVELS_BY_ORDER, ENTRANCES_INFINITE_GADGET_FLOAT_APPLICABLE) if TYPE_CHECKING: from ..AE3_Client import AE3Context from .. import AE3World, AE3Options class GoalTarget: name : str = "Empty Goal" description : str = "A Generic Goal Target" locations : set[str] = {} location_ids : set[int] = {} amount : int = 0 def __init__(self, amount : int = 0, excluded_stages : list[str] = None, excluded_locations : list[str] = None, shop_mode : int = 1): if not self.locations: return if excluded_locations is None: excluded_locations = [] if excluded_stages is None: excluded_stages = [] # Add Extra Locations self.locations = {*self.locations} # Exclude Specified Locations if any locations_excluded : list[str] = excluded_locations if excluded_locations else [] if excluded_stages: for stage in excluded_stages: locations_excluded.extend( locations for locations in MONKEYS_INDEX.get(stage, []) ) locations_excluded.extend( locations for locations in CAMERAS_INDEX.get(stage, []) ) locations_excluded.extend( locations for locations in CELLPHONES_INDEX.get(stage, []) ) # Exclude Shop Item Locations that are not used if shop_mode == 2: locations_excluded.extend(set(SHOP_UNIQUE_MASTER).difference(SHOP_PERSISTENT_MASTER)) else: locations_excluded.extend(set(SHOP_COLLECTION_MASTER).difference(SHOP_PERSISTENT_MASTER)) if locations_excluded: actual : str = "Actual: " if self.locations else "[ No Set Locations ] |" for location in self.locations: actual += f"{location}, " self.locations.difference_update(set(locations_excluded)) if len(self.locations) <= 0: excluded : str = "Excluded: " for stage in excluded_stages: excluded += f"{stage}, " for location in locations_excluded: excluded += f"{location} | " raise OptionError(f"AE3 > GoalTarget: There are no goal locations available to check. " f"They might have been explicitly excluded. Please reduce the excluded locations.", f"{actual}", f"{excluded}") elif len(self.locations) < self.amount: warn("AE3 > GoalTarget :" " Number of Locations required to check for Goal has been reduced due to excluded locations.") self.amount = len(self.locations) self.location_ids = {generate_name_to_id()[location] for location in self.locations} if amount and amount < 101: mod: float = amount / 100 total_locs: int = len(self.locations) new_total: int = int(math.ceil(total_locs * mod)) if not self.amount or self.amount is None: self.amount = total_locs if not amount else min(new_total, total_locs) else: self.amount = max(min(total_locs, new_total), 0) elif not self.amount or self.amount is None: self.amount = len(self.locations) def __bool__(self) -> bool: return bool(self.amount) def __str__(self): return self.name + "\n [ " + self.description + " ]" def exclude(self, locations : list[str] = None): if locations is None or not locations: self.locations = { location for location in self.locations if location not in locations } self.location_ids = {generate_name_to_id()[location] for location in self.locations} def append(self, *locations : str): self.locations = { * self.locations, *locations } async def check(self, ctx : 'AE3Context'): checked: set[int] = ctx.locations_checked.copy() if len(self.location_ids.intersection(checked)) >= self.amount and not ctx.game_goaled: await ctx.goal() def get_progress(self, ctx : 'AE3Context') -> int: checked: set[int] = ctx.locations_checked progress : int = len(self.location_ids.intersection(checked)) return progress def get_remaining(self, ctx : 'AE3Context') -> list[str]: checked: set[int] = ctx.locations_checked progressed : set[int] = self.location_ids.intersection(checked) name_to_id : dict[str, int] = generate_name_to_id() remaining : list[str] = [ l for l in self.locations if name_to_id[l] not in progressed] return remaining def verify(self, state : CollectionState, player : int) -> bool: checks : int = 0 for location in self.locations: if not state.can_reach_location(location, player): continue checks += 1 if checks >= self.amount: return True return False def enact(self) -> Callable[[CollectionState, int], bool]: return lambda state, player : self.verify(state, player) @dataclass class PostGameCondition: name : str = "No Post Game Access Rule" description : str = "" passed : bool = False locations : dict[str, set[str]] = field(default_factory=dict) location_ids: dict[str, set[int]] = field(default_factory=dict) amounts : dict[str, int] = field(default_factory=dict) location_categories : ClassVar[list[str]] = [APHelper.monkey.value, APHelper.bosses.value, APHelper.camera.value, APHelper.cellphone.value, APHelper.shop.value] def __init__(self, amounts : dict[str, int], excluded_regions : list[str] = None, excluded_locations : list[str] = None, shop_mode : int = 1): # Check if at least one Post Game Condition is enabled if not amounts or all(_ <= 0 for _ in [*amounts.values()]): raise OptionError("AE3 > PostGameCondition: At least one Post-Game Condition must be enabled!") # Initialize self.passed : bool = False self.locations = {} self.location_ids = {} if excluded_locations is None: excluded_locations = [] if excluded_regions is None: excluded_regions = [] # Set amounts self.amounts = amounts # Get locations from region to exclude if excluded_regions: for region in excluded_regions: excluded_locations.extend(LOCATIONS_INDEX.get(region, [])) # Exclude Shop Item Locations that are not used if shop_mode == 2: excluded_locations.extend(set(SHOP_UNIQUE_MASTER).difference(SHOP_PERSISTENT_MASTER)) else: excluded_locations.extend(set(SHOP_COLLECTION_MASTER).difference(SHOP_PERSISTENT_MASTER)) # Define valid locations for checking to_id : dict[str, int] = generate_name_to_id() for category in self.location_categories: if category in self.amounts: location_list : set[str] = {*LOCATIONS_DIRECTORY.get(category, [])} if category == APHelper.monkey.value: location_list -= {Loc.boss_tomoki.value} elif category == APHelper.cellphone.value: location_list = {Cellphone_Name_to_ID[loc] for loc in location_list} location_list -= set(excluded_locations) self.locations[category] = {*location_list} # Lower required amount if there are enough excluded locations to make the initial value impossible if self.amounts[category] > len(location_list): if category == APHelper.shop.value: self.amounts[category] = min(len(location_list), len(SHOP_UNIQUE_MASTER)) else: self.amounts[category] = len(location_list) self.location_ids[category] = { to_id[loc] for loc in location_list } def verify(self, state : CollectionState, player : int, min_keys : int, break_rooms : int = 0) -> bool: if not has_enough_keys(state, player, min_keys): return False rules : set[Callable] = set() if any(category in self.amounts for category in self.location_categories[:3]): rules.update({AccessRule.CATCH, AccessRule.FLY, AccessRule.DASH, AccessRule.SHOOT}) highest_scale : float = 0.0 if APHelper.monkey.value in self.amounts: highest_scale = max(highest_scale, (self.amounts[APHelper.monkey.value] / len(self.location_ids[APHelper.monkey.value]))) if break_rooms >= 2 and self.amounts[APHelper.monkey.value] > 354: rules.add(AccessRule.MONKEY) if APHelper.camera.value in self.amounts: count : int = sum([state.can_reach_location(camera, player) for camera in self.locations[APHelper.camera.value]]) if count < self.amounts[APHelper.camera.value]: return False if APHelper.cellphone.value in self.amounts: highest_scale = max(highest_scale, len(self.location_ids[APHelper.cellphone.value]) / 20) if APHelper.keys.value in self.amounts: rules.add(has_keys((min_keys - 1) + self.amounts[APHelper.keys.value])) # Check Rules that only yields small progress if required amount is high enough highest_scale *= 100 if highest_scale > 50: rules.update({AccessRule.MAGICIAN, AccessRule.KUNGFU, AccessRule.SWIM}) if highest_scale > 80: rules.update({AccessRule.RCC, AccessRule.SLING}) # Check Rules if rules: if not all(rule(state, player) for rule in rules): return False return True def enact(self, min_keys : int, break_rooms : int = 0) -> Callable[[CollectionState, int], bool]: return lambda state, player : self.verify(state, player, min_keys, break_rooms) def check(self, ctx : 'AE3Context') -> bool: # If already passed, use the cached status instead of checking everything again if self.passed: return True total_checked : set[int] = ctx.locations_checked.copy() passed : bool = True # Count Checked Locations needed for category in self.location_categories: if not category in self.amounts or not category in self.location_ids: continue passed = passed and len(total_checked.intersection((self.location_ids[category]))) >= self.amounts[category] # Check for keys required on post if APHelper.keys.value in self.amounts: passed = passed and ctx.keys - (len(ctx.progression.progression) - 3) >= self.amounts[APHelper.keys.value] if passed and ctx.keys >= len(ctx.progression.progression) - 3: self.passed = True return True self.passed = False return False def bypass(self): self.passed = True def get_progress(self, ctx : 'AE3Context') -> dict[str, list[int]]: total_checked: set[int] = ctx.locations_checked.copy() progress : dict[str, list[int]] = {} for category in self.location_categories: if category in self.amounts: progress[category] = [len(total_checked.intersection((self.location_ids[category]))), self.amounts[category]] if APHelper.keys.value in self.amounts: progress[APHelper.keys.value] = [ctx.keys, len(ctx.progression.progression[:-3]) + self.amounts[APHelper.keys.value]] return progress def get_remaining(self, ctx : 'AE3Context') -> dict[str, list[str]]: checked : set[int] = ctx.locations_checked remaining : dict[str, list[str]] = {} for category in self.location_categories: if category in self.amounts: remaining[category] = [location for location in self.locations[category] if generate_name_to_id()[location] not in checked] return remaining class LogicPreference: """ Base Class for defined RuleTypes. RuleTypes determine the kinds of access rules locations or regions have based on a preferred play style Attributes: monkey_rules : Rulesets for Monkeys event_rules : Rulesets for Events entrance_rules : Rulesets for Entrances blacklisted_entrances : Entrances that are excepted from generation and will never be in logic small_starting_channels : Channels with Starting Areas too small that must be actively swapped out from being an early level when Shuffle Channel is enabled. """ monkey_rules : dict[str, Rulesets] = {} event_rules : dict[str, Rulesets] = {} entrance_rules : dict[str, Rulesets] = {} blacklisted_entrances : list[str] = [] default_critical_rule : Set[Callable] = [AccessRule.CATCH] small_starting_channels : list[int] def __init__(self): self.monkey_rules : dict[str, Rulesets] = {} self.event_rules : dict[str, Rulesets] = {} self.entrance_rules : dict[str, Rulesets] = {} self.blacklisted_entrances : list[str] = [] self.minimum_requirements : set[Callable] = set() self.edge_requirements : set[Callable] = set() self.edge_percentage : int = 0 # Get all Access Rules within the channel def get_channel_clear_rules(self, *regions : str) -> Rulesets: rules : Rulesets = Rulesets() if not regions in STAGES_DIRECTORY: return rules for region in regions: # Entrance Rules if region in self.entrance_rules: rules.update(self.entrance_rules[region]) # Monkey Rules rules.critical.update(self.default_critical_rule) if region in MONKEYS_INDEX: for monkey in MONKEYS_INDEX[region]: if monkey in self.monkey_rules: rules.rules.extend(self.monkey_rules[monkey].rules) return rules def apply_unlimited_gadget_float_rules(self, hds: bool, mqj: bool): if not hds and not mqj: return rules: list = [] if hds: rules.append(AccessRule.G_FLOAT) if mqj: rules.append(AccessRule.QJ) for monkey in MONKEYS_INFINITE_GADGET_FLOAT_APPLICABLE: self.monkey_rules.setdefault(monkey, Rulesets()).update(Rulesets(rules)) for event in EVENTS_INFINITE_GADGET_FLOAT_APPLICABLE: self.event_rules.setdefault(event, Rulesets()).update(Rulesets(rules)) for entrance in ENTRANCES_INFINITE_GADGET_FLOAT_APPLICABLE: self.entrance_rules.setdefault(entrance, Rulesets()).update(Rulesets(rules)) def apply_timed_kung_fu_rule(self, base_duration: int, morph_extensions_enabled: bool): # Should only be Implemented by Expert Logic return def apply_timed_morph_float(self, base_duration: int, morph_extensions_enabled: bool): # Should only be Implemented by Expert Logic return def apply_option_logic(self, options: 'AE3Options'): # Should be implemented by each LogicPreference as needed return # [<--- LOGIC PREFERENCES --->] class Hard(LogicPreference): """ RuleType for a hard experience. The player is assumed to play the game with in-depth knowledge of the game, except any glitches major oversights. """ def __init__(self): super().__init__() self.small_starting_channels = [6, 18, 20, 22, 23] self.blacklisted_entrances = [ Stage.entrance_bay_e1e3.value, Stage.entrance_bay_e3e4.value, Stage.entrance_tomo_f2f.value ] self.monkey_rules.update({ # Seaside Loc.seaside_morella.value : Rulesets(AccessRule.SHOOT, AccessRule.FLY, AccessRule.MAGICIAN), # Woods Loc.woods_kreemon.value : Rulesets(AccessRule.ATTACK), # Castle Loc.castle_monga.value : Rulesets(AccessRule.SHOOT, AccessRule.GLIDE, AccessRule.KUNGFU, AccessRule.MAGICIAN), # Studio Loc.studio_minoh.value : Rulesets(AccessRule.SHOOT, AccessRule.GLIDE, AccessRule.MAGICIAN), Loc.studio_monta.value : Rulesets(AccessRule.SHOOT, AccessRule.GLIDE, AccessRule.MAGICIAN), # Onsen Loc.onsen_chabimon.value : Rulesets(AccessRule.RCC, AccessRule.MONKEY), # Snowfesta Loc.snowfesta_kimisuke.value : Rulesets(AccessRule.SHOOT), Loc.snowfesta_mitsuro.value : Rulesets(AccessRule.SHOOT), # Heaven Loc.heaven_chomon.value : Rulesets(AccessRule.RCC, AccessRule.MONKEY), # Toyhouse Loc.toyhouse_monto.value : Rulesets(AccessRule.SHOOT, AccessRule.FLY), Loc.toyhouse_mokitani.value : Rulesets(AccessRule.RCC), # Iceland Loc.iceland_jolly_mon.value : Rulesets(AccessRule.SLING), Loc.iceland_hikkori.value : Rulesets(AccessRule.SLING), Loc.iceland_rammy.value : Rulesets(AccessRule.SLING), # Arabian Loc.arabian_minimon.value : Rulesets(AccessRule.MAGICIAN, AccessRule.DASH), # Asia Loc.bay_kazuo.value : Rulesets(AccessRule.NINJA, AccessRule.HERO, [AccessRule.SHOOT, AccessRule.SWIM]), Loc.asia_mohcha.value : Rulesets(AccessRule.RCC, AccessRule.MONKEY), Loc.asia_takumon.value : Rulesets(AccessRule.SWIM, AccessRule.CATCH_LONG, [AccessRule.KUNGFU, AccessRule.RCC], [AccessRule.KUNGFU, AccessRule.CLUB], [AccessRule.KUNGFU, AccessRule.SHOOT],), Loc.asia_ukki_ether.value : Rulesets(AccessRule.SWIM, AccessRule.HERO), # Plane Loc.plane_mukita.value : Rulesets(AccessRule.MAGICIAN), # Hong Loc.hong_ukki_chan.value : Rulesets(AccessRule.SHOOT), Loc.hong_uki_uki.value : Rulesets(AccessRule.SHOOT, AccessRule.FLY), Loc.hong_muki_muki.value : Rulesets(AccessRule.SHOOT, AccessRule.FLY), Loc.hong_bankan.value : Rulesets(AccessRule.GLIDE), Loc.hong_sukei.value : Rulesets(AccessRule.SHOOT, AccessRule.NINJA), # Bay Loc.bay_shiny_pete.value : Rulesets(AccessRule.SHOOT), Loc.bay_gimo.value : Rulesets(AccessRule.SHOOT), # Tomo Loc.tomo_riley.value : Rulesets([AccessRule.KUNGFU, AccessRule.MAGICIAN]), Loc.tomo_pipo_ron.value : Rulesets(AccessRule.KUNGFU), # Space Loc.space_rokkun.value : Rulesets(AccessRule.RCC), Loc.space_sal_3000.value : Rulesets(AccessRule.SHOOT), }) self.event_rules.update({ # Studio Events.studio_b1_button.value : Rulesets(AccessRule.SHOOT, AccessRule.GLIDE, AccessRule.MAGICIAN), # Halloween Events.halloween_b1_jumbo_robo_shoot.value : Rulesets(AccessRule.SHOOT), # Edotown Events.edotown_e_scroll.value : Rulesets(AccessRule.CLUB, AccessRule.HOOP, AccessRule.MORPH), # Iceland Events.iceland_e_button.value : Rulesets(AccessRule.SLING), # Arabian Events.arabian_c_golden_mon.value : Rulesets(AccessRule.CATCH), # Asia Events.asia_e1_button.value : Rulesets(AccessRule.RCC, AccessRule.SHOOT, critical={AccessRule.FLY}), # Hong Events.hong_b_kungfu.value : Rulesets(AccessRule.KUNGFU), # Bay Events.bay_e1_button.value : Rulesets(AccessRule.SHOOT), # Tomo Events.tomo_e2_kungfu.value : Rulesets(AccessRule.KUNGFU), Events.tomo_g_button.value : Rulesets(AccessRule.RCC), # Space Events.space_g_button.value : Rulesets(AccessRule.SWIM), Events.space_f1_kungfu.value : Rulesets(AccessRule.KUNGFU), }) self.entrance_rules.update({ # Seaside Stage.entrance_seaside_ac.value : Rulesets(AccessRule.MONKEY), # Woods Stage.entrance_woods_ad.value : Rulesets(AccessRule.MONKEY), # Castle Stage.entrance_castle_a1a.value : Rulesets(AccessRule.ATTACK), Stage.entrance_castle_aa2.value : Rulesets(event_invoked(Events.castle_a2_button.value)), Stage.entrance_castle_b1b.value : Rulesets(event_invoked(Events.castle_b_clapper.value)), Stage.entrance_castle_be.value : Rulesets(AccessRule.MONKEY), # Ciscocity Stage.entrance_ciscocity_ad.value : Rulesets(AccessRule.DASH, AccessRule.RCC, AccessRule.KUNGFU), Stage.entrance_ciscocity_ad_2.value : Rulesets(event_invoked(Events.ciscocity_d_exit.value)), Stage.entrance_ciscocity_ce.value : Rulesets(AccessRule.MONKEY), Stage.entrance_ciscocity_c1c.value : Rulesets(event_invoked(Events.ciscocity_c_button.value)), Stage.entrance_ciscocity_cc1.value : Rulesets(event_invoked(Events.ciscocity_c_button.value)), # Studio Stage.entrance_studio_aa1.value : Rulesets(event_invoked(Events.studio_a1_button.value)), Stage.entrance_studio_aa2.value : Rulesets(event_invoked(Events.studio_a2_button.value)), Stage.entrance_studio_b1b2.value : Rulesets(event_invoked(Events.studio_b1_button.value)), Stage.entrance_studio_b2b.value : Rulesets(event_invoked(Events.studio_b1_button.value)), Stage.entrance_studio_bb2.value : Rulesets(event_invoked(Events.studio_b1_button.value)), Stage.entrance_studio_ff1.value : Rulesets(event_invoked(Events.studio_f_tele_robo.value)), Stage.entrance_studio_f1f.value : Rulesets(event_invoked(Events.studio_f_tele_robo.value)), Stage.entrance_studio_dg.value : Rulesets(AccessRule.MONKEY), Stage.entrance_studio_dd2.value : Rulesets(AccessRule.SHOOT), # Halloween Stage.entrance_halloween_aa1.value : Rulesets(AccessRule.SWIM, AccessRule.HERO), Stage.entrance_halloween_a1a.value : Rulesets(AccessRule.SWIM, AccessRule.HERO), Stage.entrance_halloween_bb1.value : Rulesets( event_invoked(Events.halloween_b_jumbo_robo.value), event_invoked(Events.halloween_b1_jumbo_robo_shoot.value)), Stage.entrance_halloween_b1b.value: Rulesets( event_invoked(Events.halloween_b_jumbo_robo.value), event_invoked(Events.halloween_b1_jumbo_robo_shoot.value)), Stage.entrance_halloween_c1c.value : Rulesets(AccessRule.SWIM, AccessRule.HERO), Stage.entrance_halloween_cc1.value : Rulesets(AccessRule.SWIM, AccessRule.HERO), Stage.entrance_halloween_cc2.value : Rulesets(AccessRule.SWIM, AccessRule.GLIDE), Stage.entrance_halloween_c2c.value : Rulesets(AccessRule.SWIM, AccessRule.GLIDE), Stage.entrance_halloween_d1e.value : Rulesets(AccessRule.MONKEY), # Western Stage.entrance_western_ee1.value : Rulesets(AccessRule.GLIDE, AccessRule.SHOOT, AccessRule.KUNGFU, AccessRule.MAGICIAN), Stage.entrance_western_ec.value : Rulesets(AccessRule.MONKEY), # Onsen Stage.entrance_onsen_a1a.value : Rulesets(event_invoked(Events.onsen_a_button.value)), Stage.entrance_onsen_a2a.value : Rulesets(event_invoked(Events.onsen_a_button.value)), Stage.entrance_onsen_a1b1.value : Rulesets(AccessRule.DASH, AccessRule.RCC), Stage.entrance_onsen_a2b1.value : Rulesets(AccessRule.DASH, AccessRule.RCC), Stage.entrance_onsen_b1b.value : Rulesets(AccessRule.GLIDE, AccessRule.KUNGFU, [AccessRule.RCC, AccessRule.SWIM]), Stage.entrance_onsen_be.value : Rulesets(AccessRule.FLY), Stage.entrance_onsen_bd1.value : Rulesets(AccessRule.SHOOT, [AccessRule.RCC, AccessRule.ATTACK]), Stage.entrance_onsen_bd.value : Rulesets(AccessRule.FLY), Stage.entrance_onsen_dd1.value : Rulesets(AccessRule.RCC, AccessRule.DASH), Stage.entrance_onsen_d1d.value : Rulesets(AccessRule.RCC), Stage.entrance_onsen_dc.value : Rulesets(AccessRule.MONKEY), # Snowfesta Stage.entrance_snowfesta_ab.value : Rulesets(AccessRule.RCC, AccessRule.DASH), Stage.entrance_snowfesta_ag.value : Rulesets(AccessRule.MONKEY), Stage.entrance_snowfesta_ce_2.value : Rulesets(event_invoked(Events.snowfesta_e_bell.value)), Stage.entrance_snowfesta_ec.value : Rulesets(event_invoked(Events.snowfesta_e_bell.value)), # Edotown Stage.entrance_edotown_b1b2.value : Rulesets(AccessRule.NINJA), Stage.entrance_edotown_b2b1.value : Rulesets([event_invoked(Events.edotown_b1_button.value), AccessRule.NINJA], AccessRule.SWIM, AccessRule.HERO), Stage.entrance_edotown_be.value : Rulesets(event_invoked(Events.edotown_e_scroll.value)), Stage.entrance_edotown_df.value : Rulesets(AccessRule.MONKEY), Stage.entrance_edotown_eb.value : Rulesets(event_invoked(Events.edotown_e_scroll.value)), # Heaven Stage.entrance_heaven_ab.value : Rulesets(AccessRule.GLIDE, AccessRule.KUNGFU, AccessRule.MAGICIAN), Stage.entrance_heaven_ba.value : Rulesets(AccessRule.GLIDE), Stage.entrance_heaven_b1b.value : Rulesets(event_invoked(Events.heaven_b_clapper.value)), Stage.entrance_heaven_ce.value : Rulesets(AccessRule.MONKEY), # Toyhouse Stage.entrance_toyhouse_ae.value : Rulesets(AccessRule.NINJA), Stage.entrance_toyhouse_ea.value : Rulesets(AccessRule.NINJA), Stage.entrance_toyhouse_dh.value : Rulesets(AccessRule.MONKEY), # Iceland Stage.entrance_iceland_a2e.value : Rulesets(event_invoked(Events.iceland_e_button.value)), Stage.entrance_iceland_ea2.value : Rulesets(event_invoked(Events.iceland_e_button.value)), Stage.entrance_iceland_ef.value : Rulesets(AccessRule.MONKEY), Stage.entrance_iceland_cb.value : Rulesets(event_invoked(Events.iceland_c_jumbo_robo.value)), # Arabian Stage.entrance_arabian_ac.value : Rulesets([AccessRule.RCC, AccessRule.SLING]), Stage.entrance_arabian_ac1.value : Rulesets(event_invoked(Events.arabian_c1_exit.value)), Stage.entrance_arabian_cc1.value : Rulesets(event_invoked(Events.arabian_c_golden_mon.value)), Stage.entrance_arabian_c1c.value : Rulesets(event_invoked(Events.arabian_c_golden_mon.value)), Stage.entrance_arabian_bg.value : Rulesets(event_invoked(Events.arabian_g_exit.value)), Stage.entrance_arabian_bf.value : Rulesets(AccessRule.MONKEY), Stage.entrance_arabian_e1e.value : Rulesets(AccessRule.MAGICIAN), # Asia Stage.entrance_asia_ab.value : Rulesets(AccessRule.SWIM, AccessRule.HERO), Stage.entrance_asia_ba.value : Rulesets(AccessRule.SWIM, AccessRule.HERO), Stage.entrance_asia_aa1.value : Rulesets(AccessRule.SWIM), Stage.entrance_asia_aa5.value : Rulesets(AccessRule.SWIM), Stage.entrance_asia_a1a.value : Rulesets(AccessRule.SWIM, AccessRule.HERO), Stage.entrance_asia_a1b2.value : Rulesets(AccessRule.SWIM, AccessRule.HERO), Stage.entrance_asia_a1a2.value : Rulesets(AccessRule.HERO), Stage.entrance_asia_a1a3.value : Rulesets(AccessRule.SWIM, [AccessRule.HERO, event_invoked(Events.asia_a2_block.value)]), Stage.entrance_asia_a1a4.value : Rulesets(AccessRule.SWIM), Stage.entrance_asia_a2a1.value : Rulesets(AccessRule.SWIM, AccessRule.HERO), Stage.entrance_asia_a2a3.value : Rulesets([event_invoked(Events.asia_a_block.value), event_invoked(Events.asia_a1_block.value), event_invoked(Events.asia_a2_block.value)], AccessRule.HERO), Stage.entrance_asia_a2a4.value : Rulesets(AccessRule.SWIM, AccessRule.HERO), Stage.entrance_asia_a3a1.value : Rulesets(AccessRule.SWIM, [AccessRule.HERO, event_invoked(Events.asia_a2_block.value)]), Stage.entrance_asia_a3a4.value : Rulesets(AccessRule.SWIM, [AccessRule.HERO, event_invoked(Events.asia_a2_block.value)]), Stage.entrance_asia_a3a2.value : Rulesets([event_invoked(Events.asia_a_block.value), event_invoked(Events.asia_a1_block.value), event_invoked(Events.asia_a2_block.value)], AccessRule.HERO), Stage.entrance_asia_a3e.value : Rulesets([event_invoked(Events.asia_a_block.value), event_invoked(Events.asia_a1_block.value), event_invoked(Events.asia_a2_block.value)]), Stage.entrance_asia_a4a1.value : Rulesets(AccessRule.SWIM), Stage.entrance_asia_a4a3.value : Rulesets(AccessRule.SWIM, [AccessRule.HERO, event_invoked(Events.asia_a2_block.value)]), Stage.entrance_asia_a4a5.value : Rulesets(AccessRule.SWIM), Stage.entrance_asia_a5a6.value : Rulesets(AccessRule.SWIM), Stage.entrance_asia_a6a5.value : Rulesets(AccessRule.SWIM), Stage.entrance_asia_bb1.value : Rulesets(AccessRule.SHOOT, AccessRule.FLY), Stage.entrance_asia_bb2.value : Rulesets(AccessRule.HERO, event_invoked(Events.asia_b2_button.value)), Stage.entrance_asia_b1b2.value : Rulesets(AccessRule.SWIM), Stage.entrance_asia_b2b1.value : Rulesets(AccessRule.SWIM), Stage.entrance_asia_d1a4.value : Rulesets(AccessRule.SWIM), Stage.entrance_asia_dd1.value : Rulesets(AccessRule.SWIM), Stage.entrance_asia_ef.value : Rulesets(AccessRule.MONKEY), Stage.entrance_asia_e1e.value : Rulesets(AccessRule.GLIDE), Stage.entrance_asia_e1e2.value : Rulesets(AccessRule.SHOOT, [AccessRule.ATTACK, AccessRule.RCC], critical={AccessRule.FLY}), Stage.entrance_asia_e2e.value : Rulesets([event_invoked(Events.asia_e1_button.value), AccessRule.SWIM]), # Plane Stage.entrance_plane_aa1.value : Rulesets(AccessRule.NINJA), Stage.entrance_plane_ac.value : Rulesets(AccessRule.RCC, AccessRule.DASH, AccessRule.KUNGFU), Stage.entrance_plane_cc1.value : Rulesets(AccessRule.MAGICIAN), Stage.entrance_plane_cg.value : Rulesets(AccessRule.MONKEY), Stage.entrance_plane_dd1.value : Rulesets(AccessRule.GLIDE, AccessRule.KUNGFU), Stage.entrance_plane_d1d.value : Rulesets(AccessRule.GLIDE, AccessRule.KUNGFU), Stage.entrance_plane_ed1.value : Rulesets(event_invoked(Events.plane_d1_clapper.value)), # Hong Stage.entrance_hong_aa1.value : Rulesets(AccessRule.KUNGFU, AccessRule.HERO), Stage.entrance_hong_a1a2.value : Rulesets(AccessRule.HIT), Stage.entrance_hong_bb1.value : Rulesets(AccessRule.KUNGFU), Stage.entrance_hong_b1b.value : Rulesets(event_invoked(Events.hong_b_kungfu.value)), Stage.entrance_hong_bb2.value : Rulesets(event_invoked(Events.hong_b2_button.value)), Stage.entrance_hong_b1f.value : Rulesets(AccessRule.MONKEY), Stage.entrance_hong_b2b.value : Rulesets(event_invoked(Events.hong_b2_button.value)), Stage.entrance_hong_b1c.value : Rulesets(AccessRule.KUNGFU), Stage.entrance_hong_cc1.value : Rulesets(AccessRule.GLIDE), Stage.entrance_hong_cc2.value : Rulesets(AccessRule.FLYER), Stage.entrance_hong_c1c.value : Rulesets(AccessRule.GLIDE), Stage.entrance_hong_c1c2.value : Rulesets(AccessRule.NINJA, AccessRule.HERO), Stage.entrance_hong_ee1.value : Rulesets(AccessRule.KUNGFU), Stage.entrance_hong_dg.value : Rulesets(AccessRule.KUNGFU), # Bay Stage.entrance_bay_aa1.value : Rulesets([AccessRule.SHOOT, AccessRule.SWIM], AccessRule.HERO), Stage.entrance_bay_a1a.value : Rulesets(AccessRule.SWIM, AccessRule.HERO), Stage.entrance_bay_a1b.value : Rulesets(AccessRule.RCC), Stage.entrance_bay_a1a2.value : Rulesets(AccessRule.SWIM), Stage.entrance_bay_a2a1.value : Rulesets(AccessRule.SWIM), Stage.entrance_bay_a2a3.value : Rulesets(AccessRule.SWIM), Stage.entrance_bay_a2e.value : Rulesets(AccessRule.SWIM), Stage.entrance_bay_a3a2.value : Rulesets(AccessRule.SWIM), Stage.entrance_bay_a3a6.value : Rulesets(event_invoked(Events.bay_a7_button.value)), Stage.entrance_bay_a4d1.value : Rulesets(AccessRule.SLING), Stage.entrance_bay_a6f.value : Rulesets(AccessRule.MONKEY), Stage.entrance_bay_d1d.value : Rulesets(AccessRule.KUNGFU), Stage.entrance_bay_ea2.value : Rulesets(AccessRule.SWIM), Stage.entrance_bay_ee1.value : Rulesets(AccessRule.SHOOT), Stage.entrance_bay_ee2.value : Rulesets(event_invoked(Events.bay_e1_button.value)), Stage.entrance_bay_e1e2.value : Rulesets(AccessRule.HIT), Stage.entrance_bay_e2e3.value : Rulesets([AccessRule.FLY, AccessRule.KUNGFU]), # Tomo Stage.entrance_tomo_a1a.value : Rulesets(AccessRule.GLIDE, AccessRule.MAGICIAN, AccessRule.KUNGFU), Stage.entrance_tomo_e1i.value : Rulesets(AccessRule.MONKEY), Stage.entrance_tomo_e2e3.value : Rulesets(AccessRule.KUNGFU), Stage.entrance_tomo_e3e2.value : Rulesets(event_invoked(Events.tomo_e2_kungfu.value)), Stage.entrance_tomo_f1f2.value : Rulesets(AccessRule.NINJA, AccessRule.HERO), Stage.entrance_tomo_f2f1.value : Rulesets(AccessRule.NINJA, AccessRule.HERO), Stage.entrance_tomo_gg1.value : Rulesets(AccessRule.KUNGFU), Stage.entrance_tomo_g1f.value : Rulesets(event_invoked(Events.tomo_g_button.value)), Stage.entrance_tomo_h1h.value : Rulesets(AccessRule.GLIDE, AccessRule.KUNGFU), Stage.entrance_tomo_ha.value : Rulesets(AccessRule.SHOOT), # Space Stage.entrance_space_bi.value : Rulesets([event_invoked(Events.space_e_button.value), event_invoked(Events.space_f2_button.value), event_invoked(Events.space_g1_button.value), event_invoked(Events.space_d_button.value)]), Stage.entrance_space_e1e.value : Rulesets(event_invoked(Events.space_e_button.value)), Stage.entrance_space_ee1_2.value : Rulesets(event_invoked(Events.space_e_button.value)), Stage.entrance_space_e1e_2.value : Rulesets([AccessRule.RCC, AccessRule.ATTACK]), Stage.entrance_space_ee1.value : Rulesets(event_invoked(Events.space_e_button.value)), Stage.entrance_space_eh.value : Rulesets(AccessRule.MONKEY), Stage.entrance_space_gg1.value : Rulesets(event_invoked(Events.space_g1_button.value)), Stage.entrance_space_gg1_2.value : Rulesets(AccessRule.SWIM), Stage.entrance_space_g1g.value : Rulesets(event_invoked(Events.space_g_button.value)), Stage.entrance_space_g1g_2.value : Rulesets(event_invoked(Events.space_g1_button.value)), Stage.entrance_space_ff2.value : Rulesets(event_invoked(Events.space_f2_button.value)), Stage.entrance_space_ff1.value : Rulesets(AccessRule.GLIDE, AccessRule.KUNGFU), Stage.entrance_space_f1f2.value : Rulesets(AccessRule.KUNGFU), Stage.entrance_space_f2f1.value : Rulesets(event_invoked(Events.space_f1_kungfu.value)), Stage.entrance_space_f2f.value : Rulesets(event_invoked(Events.space_f2_button.value)), Stage.entrance_space_dd.value : Rulesets(AccessRule.ATTACK), Stage.entrance_space_dd_2.value : Rulesets(AccessRule.ATTACK), Stage.entrance_space_ib.value : Rulesets([event_invoked(Events.space_e_button.value), event_invoked(Events.space_f2_button.value), event_invoked(Events.space_g1_button.value), event_invoked(Events.space_d_button.value)]), Stage.entrance_space_jj1.value : Rulesets(AccessRule.GLIDE), Stage.entrance_space_j1j.value : Rulesets(AccessRule.GLIDE), }) class Expert(Hard): """ RuleType for players that want to take advantage of glitches (excluding very powerful ones such as HDS) """ def __init__(self): super().__init__() self.blacklisted_entrances = [] self.monkey_rules.update({ Loc.seaside_morella.value: Rulesets( AccessRule.SHOOT, AccessRule.FLY, AccessRule.MAGICIAN, AccessRule.KUNGFU, AccessRule.QJ, AccessRule.G_FLOAT, AccessRule.G_FLOAT_M ), Loc.castle_monga.value: Rulesets( AccessRule.SHOOT, AccessRule.GLIDE, AccessRule.MAGICIAN, AccessRule.KUNGFU, AccessRule.QJ, AccessRule.G_FLOAT_M, ), Loc.studio_minoh.value: Rulesets( AccessRule.SHOOT, AccessRule.GLIDE, AccessRule.MAGICIAN, AccessRule.QJ, AccessRule.G_FLOAT_M, ), Loc.studio_monta.value: Rulesets( AccessRule.SHOOT, AccessRule.GLIDE, AccessRule.MAGICIAN, AccessRule.QJ, AccessRule.G_FLOAT_M, ), Loc.toyhouse_monto.value: Rulesets( AccessRule.SHOOT, AccessRule.NINJA, AccessRule.FLYER, AccessRule.KUNGFU, AccessRule.QJ, AccessRule.G_FLOAT_M, ), Loc.toyhouse_mokitani.value: Rulesets( AccessRule.RCC, AccessRule.FLYER, AccessRule.NINJA ), Loc.snowfesta_mitsuro.value: Rulesets( AccessRule.SHOOT, AccessRule.QJ, AccessRule.G_FLOAT, AccessRule.G_FLOAT_M ), Loc.asia_baku.value: Rulesets( [AccessRule.SHOOT, AccessRule.SWIM], AccessRule.NINJA, AccessRule.HERO, AccessRule.KUNGFU ), Loc.asia_takumon.value: Rulesets( AccessRule.SWIM, AccessRule.NINJA, AccessRule.COWBOY, AccessRule.HERO, AccessRule.KUNGFU ), Loc.asia_ukki_ether.value: Rulesets( AccessRule.SWIM, AccessRule.HERO, AccessRule.KUNGFU ), Loc.hong_uki_uki.value: Rulesets( AccessRule.SHOOT, AccessRule.FLY, AccessRule.QJ, AccessRule.G_FLOAT, AccessRule.G_FLOAT_M, ), Loc.hong_muki_muki.value: Rulesets( AccessRule.SHOOT, AccessRule.FLY, AccessRule.QJ, AccessRule.G_FLOAT, AccessRule.G_FLOAT_M, ), Loc.hong_bankan.value: Rulesets( AccessRule.GLIDE, AccessRule.KUNGFU ), Loc.hong_sukei.value: Rulesets( AccessRule.SHOOT, AccessRule.FLY ), Loc.tomo_riley.value: Rulesets( [AccessRule.KUNGFU, AccessRule.MAGICIAN], AccessRule.MAGICIAN ), Loc.space_rokkun.value: Rulesets( AccessRule.RCC, AccessRule.COWBOY ) }) self.event_rules.update({ Events.studio_b1_button.value: Rulesets( AccessRule.SHOOT, AccessRule.GLIDE, AccessRule.MAGICIAN, AccessRule.BOOST_JUMP, AccessRule.KUNGFU, AccessRule.QJ, AccessRule.G_FLOAT, AccessRule.G_FLOAT_M ), Events.asia_e1_button.value: Rulesets( [AccessRule.FLY, AccessRule.SHOOT], [AccessRule.FLY, AccessRule.RCC], [AccessRule.SHOOT, AccessRule.SWIM, AccessRule.KUNGFU], [AccessRule.RCC, AccessRule.SWIM, AccessRule.KUNGFU], AccessRule.QJ, AccessRule.G_FLOAT, AccessRule.G_FLOAT_M ), Events.space_g_button.value: Rulesets(AccessRule.SWIM, AccessRule.HERO), }) self.entrance_rules.update({ Stage.entrance_seaside_ac.value: Rulesets( AccessRule.MONKEY, AccessRule.KUNGFU ), Stage.entrance_castle_aa2.value: Rulesets( event_invoked(Events.castle_a2_button.value), AccessRule.KUNGFU ), Stage.entrance_studio_b1b2.value: Rulesets( event_invoked(Events.studio_b1_button.value), AccessRule.MAGICIAN, AccessRule.BOOST_JUMP, AccessRule.KUNGFU, AccessRule.QJ, AccessRule.G_FLOAT, AccessRule.G_FLOAT_M ), Stage.entrance_halloween_c1c.value: Rulesets( AccessRule.SWIM, AccessRule.HERO, AccessRule.G_FLOAT_M, ), Stage.entrance_halloween_cc1.value: Rulesets( AccessRule.SWIM, AccessRule.HERO, AccessRule.G_FLOAT_M, ), Stage.entrance_halloween_cc2.value: Rulesets( AccessRule.SWIM, AccessRule.GLIDE, AccessRule.KUNGFU, AccessRule.QJ, AccessRule.G_FLOAT, AccessRule.G_FLOAT_M ), Stage.entrance_halloween_c2c.value: Rulesets( AccessRule.SWIM, AccessRule.GLIDE, AccessRule.KUNGFU, AccessRule.QJ, AccessRule.G_FLOAT, AccessRule.G_FLOAT_M ), Stage.entrance_western_ec.value: Rulesets( AccessRule.KUNGFU, AccessRule.MONKEY, ), Stage.entrance_western_ee1.value: Rulesets( AccessRule.GLIDE, AccessRule.SHOOT, AccessRule.KUNGFU, AccessRule.MAGICIAN, AccessRule.BOOST_JUMP, AccessRule.QJ, AccessRule.G_FLOAT, AccessRule.G_FLOAT_M ), Stage.entrance_onsen_b1b.value: Rulesets( [AccessRule.RCC, AccessRule.SWIM], AccessRule.GLIDE, AccessRule.KUNGFU, AccessRule.QJ, AccessRule.G_FLOAT, AccessRule.G_FLOAT_M ), Stage.entrance_onsen_be.value: Rulesets( AccessRule.FLY, AccessRule.QJ, AccessRule.G_FLOAT, AccessRule.G_FLOAT_M ), Stage.entrance_snowfesta_ag.value: Rulesets( AccessRule.MONKEY, AccessRule.KUNGFU, AccessRule.HERO, AccessRule.BOOST_FLY ), Stage.entrance_snowfesta_dg.value: Rulesets( AccessRule.MONKEY, AccessRule.KUNGFU, [AccessRule.QJ, AccessRule.G_FLOAT] ), Stage.entrance_edotown_be.value: Rulesets( event_invoked(Events.edotown_e_scroll.value), AccessRule.NINJA ), Stage.entrance_edotown_c1c.value: Rulesets( AccessRule.NINJA, AccessRule.HERO, AccessRule.KUNGFU, AccessRule.QJ, AccessRule.G_FLOAT_M ), Stage.entrance_edotown_cc1.value: Rulesets( AccessRule.NINJA, AccessRule.HERO, AccessRule.KUNGFU, AccessRule.QJ, AccessRule.G_FLOAT_M ), Stage.entrance_heaven_ab.value: Rulesets( AccessRule.GLIDE, AccessRule.KUNGFU, AccessRule.MAGICIAN, AccessRule.QJ, AccessRule.G_FLOAT, AccessRule.G_FLOAT_M, ), Stage.entrance_heaven_ba.value: Rulesets( AccessRule.GLIDE, AccessRule.KUNGFU, AccessRule.QJ, AccessRule.G_FLOAT, AccessRule.G_FLOAT_M, ), Stage.entrance_arabian_e1e.value: Rulesets( AccessRule.MAGICIAN, AccessRule.FLY ), Stage.entrance_asia_ab.value: Rulesets( AccessRule.SWIM, AccessRule.HERO, AccessRule.KUNGFU ), Stage.entrance_asia_ba.value: Rulesets( AccessRule.SWIM, AccessRule.HERO, AccessRule.KUNGFU ), Stage.entrance_asia_aa5.value: Rulesets( AccessRule.SWIM, AccessRule.KUNGFU, ), Stage.entrance_asia_a2a1.value: Rulesets( AccessRule.SWIM, AccessRule.HERO, AccessRule.KUNGFU, ), Stage.entrance_asia_a1b2.value: Rulesets( AccessRule.SWIM, AccessRule.HERO, AccessRule.KUNGFU, ), Stage.entrance_asia_b2a1.value: Rulesets( AccessRule.SWIM, AccessRule.HERO, AccessRule.KUNGFU, AccessRule.QJ, AccessRule.G_FLOAT_M, ), Stage.entrance_asia_a1a3.value: Rulesets( AccessRule.SWIM, [event_invoked(Events.asia_a_block.value), event_invoked(Events.asia_a1_block.value), event_invoked(Events.asia_a2_block.value), AccessRule.HERO], [event_invoked(Events.asia_a_block.value), event_invoked(Events.asia_a1_block.value), event_invoked(Events.asia_a2_block.value), AccessRule.KUNGFU] ), Stage.entrance_asia_a3a1.value: Rulesets( AccessRule.SWIM, [event_invoked(Events.asia_a_block.value), event_invoked(Events.asia_a1_block.value), event_invoked(Events.asia_a2_block.value), AccessRule.HERO], [event_invoked(Events.asia_a_block.value), event_invoked(Events.asia_a1_block.value), event_invoked(Events.asia_a2_block.value), AccessRule.KUNGFU] ), Stage.entrance_asia_a2a3.value: Rulesets( [event_invoked(Events.asia_a_block.value), event_invoked(Events.asia_a1_block.value), event_invoked(Events.asia_a2_block.value)], AccessRule.HERO, AccessRule.KUNGFU ), Stage.entrance_asia_a2a4.value: Rulesets( AccessRule.SWIM, AccessRule.HERO, AccessRule.KUNGFU ), Stage.entrance_asia_a3a4.value: Rulesets( AccessRule.SWIM, [event_invoked(Events.asia_a_block.value), event_invoked(Events.asia_a1_block.value), event_invoked(Events.asia_a2_block.value), AccessRule.HERO], [event_invoked(Events.asia_a_block.value), event_invoked(Events.asia_a1_block.value), event_invoked(Events.asia_a2_block.value), AccessRule.KUNGFU] ), Stage.entrance_asia_a4a3.value: Rulesets( AccessRule.SWIM, [event_invoked(Events.asia_a_block.value), event_invoked(Events.asia_a1_block.value), event_invoked(Events.asia_a2_block.value), AccessRule.HERO], [event_invoked(Events.asia_a_block.value), event_invoked(Events.asia_a1_block.value), event_invoked(Events.asia_a2_block.value), AccessRule.KUNGFU] ), Stage.entrance_asia_a3a2.value: Rulesets( [event_invoked(Events.asia_a_block.value), event_invoked(Events.asia_a1_block.value), event_invoked(Events.asia_a2_block.value)], AccessRule.HERO, AccessRule.KUNGFU ), Stage.entrance_asia_bb2.value: Rulesets( [event_invoked(Events.asia_b2_button.value)], AccessRule.HERO, AccessRule.BOOST_FLY, AccessRule.KUNGFU, AccessRule.G_FLOAT, AccessRule.G_FLOAT_M ), Stage.entrance_asia_e1e2.value: Rulesets( [AccessRule.SHOOT, AccessRule.FLY], [AccessRule.ATTACK, AccessRule.RCC, AccessRule.FLY], [AccessRule.HERO, has_morph_stocks(1)], AccessRule.QJ, AccessRule.G_FLOAT, AccessRule.G_FLOAT_M ), Stage.entrance_plane_cg.value: Rulesets( AccessRule.MONKEY, AccessRule.KUNGFU ), Stage.entrance_plane_dd1.value: Rulesets( AccessRule.GLIDE, AccessRule.MORPH, AccessRule.QJ, AccessRule.G_FLOAT, AccessRule.G_FLOAT_M ), Stage.entrance_plane_d1d.value: Rulesets( AccessRule.GLIDE, AccessRule.KUNGFU, AccessRule.QJ, AccessRule.G_FLOAT, AccessRule.G_FLOAT_M ), Stage.entrance_hong_aa1.value: Rulesets( AccessRule.KUNGFU, AccessRule.HERO, AccessRule.BOOST_FLY, AccessRule.QJ, AccessRule.G_FLOAT, AccessRule.G_FLOAT_M ), Stage.entrance_hong_b1f.value: Rulesets( AccessRule.MONKEY, AccessRule.KUNGFU, AccessRule.HERO ), Stage.entrance_hong_bb1.value: Rulesets( AccessRule.KUNGFU, AccessRule.BOOST_FLY, AccessRule.QJ, AccessRule.G_FLOAT, AccessRule.G_FLOAT_M ), Stage.entrance_hong_b1c.value: Rulesets( AccessRule.KUNGFU, AccessRule.BOOST_FLY, AccessRule.QJ, AccessRule.G_FLOAT, AccessRule.G_FLOAT_M ), Stage.entrance_hong_cc1.value: Rulesets( AccessRule.GLIDE, AccessRule.KUNGFU, AccessRule.QJ, AccessRule.G_FLOAT, AccessRule.G_FLOAT_M ), Stage.entrance_hong_cc2.value: Rulesets( AccessRule.FLYER, AccessRule.QJ, AccessRule.G_FLOAT ), Stage.entrance_hong_c1c.value: Rulesets( AccessRule.GLIDE, AccessRule.KUNGFU, AccessRule.QJ, AccessRule.G_FLOAT, AccessRule.G_FLOAT_M ), Stage.entrance_hong_ee1.value: Rulesets( AccessRule.KUNGFU, AccessRule.BOOST_FLY, AccessRule.QJ, AccessRule.G_FLOAT, AccessRule.G_FLOAT_M ), Stage.entrance_hong_dg.value: Rulesets( AccessRule.KUNGFU, [AccessRule.QJ, AccessRule.G_FLOAT], AccessRule.G_FLOAT_M ), Stage.entrance_bay_aa1.value: Rulesets( [AccessRule.SHOOT, AccessRule.SWIM], AccessRule.HERO, AccessRule.FLYER, AccessRule.MAGICIAN, AccessRule.KUNGFU, [AccessRule.SWIM, AccessRule.QJ], [AccessRule.SWIM, AccessRule.G_FLOAT], AccessRule.G_FLOAT_M ), Stage.entrance_bay_a1a.value: Rulesets( [AccessRule.SHOOT, AccessRule.SWIM], AccessRule.HERO, AccessRule.FLYER, AccessRule.KUNGFU, [AccessRule.SWIM, AccessRule.QJ], [AccessRule.SWIM, AccessRule.G_FLOAT], AccessRule.G_FLOAT_M ), Stage.entrance_bay_a6f.value: Rulesets( AccessRule.MONKEY, AccessRule.KUNGFU, [AccessRule.SWIM, AccessRule.FLYER] ), Stage.entrance_bay_d1d.value: Rulesets( AccessRule.KUNGFU, AccessRule.BOOST_FLY, AccessRule.G_FLOAT, AccessRule.G_FLOAT_M ), Stage.entrance_bay_ee2.value: Rulesets( AccessRule.FLYER, AccessRule.NINJA, AccessRule.QJ, [AccessRule.G_FLOAT, AccessRule.CLUB], [AccessRule.MONKEY, AccessRule.CLUB] ), Stage.entrance_bay_e3e4.value: Rulesets(AccessRule.SWIM), Stage.entrance_tomo_a1a.value: Rulesets( AccessRule.GLIDE, AccessRule.MAGICIAN, AccessRule.KUNGFU, AccessRule.QJ, AccessRule.G_FLOAT, AccessRule.G_FLOAT_M ), Stage.entrance_tomo_f1f2.value: Rulesets( AccessRule.NINJA, AccessRule.HERO, AccessRule.KUNGFU, AccessRule.G_FLOAT_M ), Stage.entrance_tomo_f2f1.value: Rulesets( AccessRule.NINJA, AccessRule.HERO, AccessRule.KUNGFU, AccessRule.G_FLOAT_M ), Stage.entrance_tomo_f2f.value: Rulesets(AccessRule.FLY), Stage.entrance_tomo_gg1.value: Rulesets( AccessRule.KUNGFU, AccessRule.BOOST_FLY, AccessRule.QJ, AccessRule.G_FLOAT, AccessRule.G_FLOAT_M ), Stage.entrance_tomo_h1h.value: Rulesets( AccessRule.GLIDE, AccessRule.KUNGFU, AccessRule.MAGICIAN, AccessRule.QJ, AccessRule.G_FLOAT, AccessRule.G_FLOAT_M ), Stage.entrance_space_bi.value: Rulesets( [event_invoked(Events.space_e_button.value), event_invoked(Events.space_f2_button.value), event_invoked(Events.space_g1_button.value), event_invoked(Events.space_d_button.value)], AccessRule.HERO, ), Stage.entrance_space_ff1.value: Rulesets( AccessRule.GLIDE, AccessRule.MORPH, AccessRule.BOOST_JUMP, AccessRule.QJ, AccessRule.G_FLOAT ) }) monkeys_to_reset: list = [ Loc.heaven_chomon.value, Loc.arabian_minimon.value, Loc.snowfesta_kimisuke.value, Loc.asia_mohcha.value, Loc.plane_mukita.value, ] for monkey in monkeys_to_reset: if monkey in self.monkey_rules: self.monkey_rules.pop(monkey) entrances_to_reset: list = [ Stage.entrance_seaside_ac.value, Stage.entrance_woods_ad.value, Stage.entrance_castle_ad.value, Stage.entrance_ciscocity_ad_2.value, Stage.entrance_ciscocity_c1c.value, Stage.entrance_ciscocity_ce.value, Stage.entrance_studio_dg.value, Stage.entrance_onsen_dd1.value, Stage.entrance_heaven_ce.value, Stage.entrance_snowfesta_ab.value, Stage.entrance_edotown_df.value, Stage.entrance_iceland_ef.value, Stage.entrance_plane_cc1.value, Stage.entrance_plane_cg.value, Stage.entrance_bay_a1b.value, Stage.entrance_tomo_e1i.value, Stage.entrance_space_e1e_2.value, Stage.entrance_space_eh.value, Stage.entrance_space_gg1.value, Stage.entrance_space_f1f2.value ] for entrance in entrances_to_reset: if entrance in self.entrance_rules: self.entrance_rules.pop(entrance) def apply_timed_kungfu_rule(self, base_duration: int, morph_extensions_enabled: bool): locations: list = [ Loc.edotown_monkibeth.value ] rules: list = [AccessRule.KUNGFU] if base_duration < 30 and morph_extensions_enabled: rules.append(has_morph_extensions()) for loc in locations: self.monkey_rules.get(loc, Rulesets()).update(Rulesets(rules)) self.entrance_rules.get(Stage.entrance_asia_e1e2.value, Rulesets()).update( Rulesets([AccessRule.SHOOT, AccessRule.SWIM, *rules], [AccessRule.ATTACK, AccessRule.RCC, AccessRule.SWIM, *rules],) ) self.event_rules.get(Events.space_g_button.value, Rulesets()).update(Rulesets(AccessRule.KUNGFU)) def apply_timed_morph_float(self, base_duration: int, morph_extensions_enabled: bool): events: list = [ Events.iceland_e_button.value ] entrances: list = [ Stage.entrance_halloween_aa1.value, Stage.entrance_halloween_a1a.value, Stage.entrance_halloween_c1c.value, Stage.entrance_halloween_cc1.value, Stage.entrance_heaven_aa1.value, Stage.entrance_heaven_a1a.value, Stage.entrance_asia_ab.value, Stage.entrance_asia_ba.value, Stage.entrance_asia_a1a2.value, Stage.entrance_asia_a1a3.value, Stage.entrance_asia_a3a1.value, Stage.entrance_asia_a2a3.value, Stage.entrance_asia_a3a2.value, Stage.entrance_asia_a2a4.value, Stage.entrance_asia_a3a4.value, Stage.entrance_hong_c1c2.value, Stage.entrance_hong_cc2.value, ] rules: list = [AccessRule.G_FLOAT_M] if base_duration < 30 and morph_extensions_enabled: rules.append(has_morph_extensions()) for e in events: self.event_rules.get(e, Rulesets()).update(Rulesets(rules)) for e in entrances: self.entrance_rules.get(e, Rulesets()).update(Rulesets(rules)) def apply_option_logic(self, options: 'AE3Options'): if options.shoppingsanity.value < 1: rules: list = [AccessRule.HERO, can_farm_boxes()] if options.farm_logic_sneaky_borgs: rules.append(can_farm_sneaky_borgs()) self.entrance_rules.get(Stage.entrance_asia_e1e2.value, Rulesets()).update(Rulesets(rules)) class Normal(Hard): """ RuleType for a normal experience. Expectations of accessibility will be the same as the vanilla game. """ def __init__(self): super().__init__() self.small_starting_channels = [6, 9, 11, 15, 18, 20, 22, 23] self.blacklisted_entrances = [ Stage.entrance_hong_cc2.value, Stage.entrance_bay_e1e3.value, Stage.entrance_bay_e3e4.value, Stage.entrance_tomo_f2f.value ] self.monkey_rules.update({ # Seaside Loc.seaside_morella.value : Rulesets(AccessRule.SHOOT, AccessRule.FLY), # Woods Loc.woods_salubon.value : Rulesets(AccessRule.HIT), Loc.woods_ukkilei.value : Rulesets(AccessRule.HIT), # Castle Loc.castle_pipo_guard.value : Rulesets(AccessRule.HIT), Loc.castle_ukkii.value : Rulesets(AccessRule.HIT), Loc.castle_sal_1000.value : Rulesets(AccessRule.HIT), Loc.castle_monga.value : Rulesets(AccessRule.SHOOT, AccessRule.GLIDE, AccessRule.MAGICIAN), # Ciscocity Loc.ciscocity_pipo_mondy.value : Rulesets(AccessRule.DASH), # Studio Loc.studio_monpii_ukkichi.value : Rulesets(AccessRule.HIT), # Halloween Loc.halloween_uikkun.value : Rulesets(AccessRule.HIT), Loc.halloween_bonbon.value : Rulesets(AccessRule.HIT, event_invoked(Events.halloween_b_jumbo_robo.value), event_invoked(Events.halloween_b1_jumbo_robo_shoot.value)), Loc.halloween_chichi.value : Rulesets(AccessRule.HIT, event_invoked(Events.halloween_b_jumbo_robo.value), event_invoked(Events.halloween_b1_jumbo_robo_shoot.value)), Loc.halloween_ukkito.value : Rulesets(AccessRule.HIT), Loc.halloween_ukkiami.value : Rulesets(AccessRule.HIT), Loc.halloween_kabochin.value : Rulesets(AccessRule.HIT), Loc.halloween_mumpkin.value : Rulesets(AccessRule.HIT), # Western Loc.western_jay_mohn.value : Rulesets(AccessRule.HIT), Loc.western_jaja_jamo.value : Rulesets(AccessRule.ATTACK, AccessRule.HOOP), Loc.western_chammy_mo.value : Rulesets(AccessRule.HIT), Loc.western_golozo.value : Rulesets(AccessRule.HIT), Loc.western_golon_moe.value : Rulesets(AccessRule.ATTACK), # Onsen Loc.onsen_chabimon.value : Rulesets(AccessRule.RCC), Loc.onsen_domobeh.value : Rulesets(AccessRule.SWIM, AccessRule.CATCH_LONG), Loc.onsen_mujakin.value : Rulesets(AccessRule.SWIM, AccessRule.CATCH_LONG), Loc.onsen_fuji_chan.value : Rulesets(AccessRule.SHOOT, AccessRule.GLIDE), # Snofesta Loc.snowfesta_konkichi.value : Rulesets(AccessRule.RADAR), Loc.snowfesta_pipotron_yellow.value : Rulesets(AccessRule.DASH), Loc.snowfesta_ukki_jii.value : Rulesets(AccessRule.HIT), # Edotown Loc.edotown_fatty_mcfats.value : Rulesets(AccessRule.HIT), Loc.edotown_walter.value : Rulesets(AccessRule.NINJA), # Heaven Loc.heaven_chomon.value : Rulesets(AccessRule.RCC), Loc.heaven_ukkido.value : Rulesets(AccessRule.ATTACK), Loc.heaven_tami.value : Rulesets(AccessRule.HIT), Loc.heaven_valuccha.value : Rulesets(AccessRule.ATTACK), # Toyhouse Loc.toyhouse_monto.value : Rulesets(AccessRule.SHOOT, AccessRule.NINJA), Loc.toyhouse_golonero.value : Rulesets(AccessRule.HIT), # Iceland Loc.iceland_kushachin.value : Rulesets(AccessRule.HIT), Loc.iceland_malikko.value : Rulesets(AccessRule.HIT), Loc.iceland_bolikko.value : Rulesets(AccessRule.HIT), Loc.iceland_iceymon.value : Rulesets(AccessRule.HIT), # Arabian Loc.arabian_minimon.value : Rulesets(AccessRule.MAGICIAN), Loc.arabian_cup_o_mon.value : Rulesets(AccessRule.HIT), # Asia Loc.asia_mohcha.value : Rulesets(AccessRule.RCC), Loc.asia_baku.value : Rulesets([AccessRule.SHOOT, AccessRule.SWIM], AccessRule.NINJA), Loc.asia_takumon.value : Rulesets(AccessRule.SWIM, AccessRule.CATCH_LONG), Loc.asia_ukki_ether.value : Rulesets(AccessRule.SWIM), # Plane Loc.plane_jeloh.value : Rulesets(AccessRule.HIT), Loc.plane_bongo.value : Rulesets(AccessRule.HIT), # Bay Loc.bay_nadamon.value : Rulesets(AccessRule.HIT), Loc.bay_nakabi.value : Rulesets(AccessRule.HIT), Loc.bay_gimi_gimi.value : Rulesets(AccessRule.HIT), Loc.bay_pokkini.value : Rulesets(AccessRule.HIT), # Tomo Loc.tomo_kichibeh.value : Rulesets(AccessRule.HIT), Loc.tomo_bonchicchi.value : Rulesets(AccessRule.HIT), Loc.tomo_mikibon.value : Rulesets(AccessRule.HIT), Loc.tomo_chimpy.value : Rulesets(AccessRule.MORPH_NO_MONKEY), Loc.tomo_kajitan.value : Rulesets(AccessRule.MORPH_NO_MONKEY), Loc.tomo_uka_uka.value : Rulesets(AccessRule.MORPH_NO_MONKEY), Loc.tomo_mil_mil.value : Rulesets(AccessRule.MORPH_NO_MONKEY), Loc.tomo_tomio.value : Rulesets(AccessRule.HIT), Loc.tomo_gario.value : Rulesets(AccessRule.HIT), Loc.tomo_sal_13.value : Rulesets(AccessRule.HIT), Loc.tomo_sal_12.value : Rulesets(AccessRule.HIT), # Space Loc.space_freet.value : Rulesets(AccessRule.HIT), Loc.space_chico.value : Rulesets(AccessRule.HIT), Loc.space_ukki_love.value : Rulesets(AccessRule.MAGICIAN), Loc.space_sal_10.value : Rulesets(AccessRule.HIT), Loc.space_sal_11.value : Rulesets(AccessRule.HIT), # Bosses Loc.boss_monkey_white.value : Rulesets(AccessRule.HIT), Loc.boss_monkey_blue.value : Rulesets(AccessRule.HIT), Loc.boss_monkey_yellow.value : Rulesets(AccessRule.HIT), Loc.boss_monkey_pink.value : Rulesets(AccessRule.HIT), Loc.boss_monkey_red.value : Rulesets(AccessRule.HIT), Loc.boss_specter.value : Rulesets([AccessRule.HIT, AccessRule.SHOOT]), Loc.boss_specter_final.value : Rulesets(AccessRule.HIT) }) self.event_rules.update({ # Castle Events.castle_b_clapper.value : Rulesets(AccessRule.HIT), # Ciscocity Events.ciscocity_c_button.value : Rulesets(AccessRule.SHOOT, AccessRule.DASH, AccessRule.RCC), # Studio Events.studio_a1_button.value : Rulesets(AccessRule.HIT), Events.studio_a2_button.value : Rulesets(AccessRule.HIT), Events.studio_b1_button.value : Rulesets(AccessRule.SHOOT, AccessRule.GLIDE), # Halloween Events.halloween_b_jumbo_robo.value : Rulesets(AccessRule.HIT), # Onsen Events.onsen_a_button.value : Rulesets(AccessRule.HIT), # Snowfesta Events.snowfesta_e_bell.value : Rulesets(AccessRule.HIT), # Edotown Events.edotown_b1_button.value : Rulesets(AccessRule.HIT), # Heaven Events.heaven_b_clapper.value : Rulesets(AccessRule.HIT), # Iceland Events.iceland_c_jumbo_robo.value : Rulesets(AccessRule.HIT), # Asia Events.asia_b2_button.value : Rulesets(AccessRule.HIT), # Plane Events.plane_d1_clapper.value : Rulesets(AccessRule.HIT), # Hong Events.hong_b2_button.value : Rulesets(AccessRule.HIT), # Bay Events.bay_a7_button.value : Rulesets(AccessRule.HIT), # Space Events.space_e_button.value : Rulesets(AccessRule.ATTACK), Events.space_g1_button.value : Rulesets(AccessRule.ATTACK), Events.space_f2_button.value : Rulesets(AccessRule.ATTACK), }) self.entrance_rules.update({ # Seaside Stage.entrance_seaside_ab.value : Rulesets(AccessRule.HIT), # Woods Stage.entrance_woods_bc.value : Rulesets(AccessRule.HIT), # Castle Stage.entrance_castle_bb1.value : Rulesets(AccessRule.HIT), # Ciscocity Stage.entrance_ciscocity_ad.value : Rulesets(AccessRule.DASH, AccessRule.RCC), # Halloween Stage.entrance_halloween_d1d2.value : Rulesets(AccessRule.HIT), # Western Stage.entrance_western_bb1.value : Rulesets(AccessRule.HIT), Stage.entrance_western_dd2.value : Rulesets(AccessRule.HIT), Stage.entrance_western_d1d2.value : Rulesets(AccessRule.HIT), Stage.entrance_western_d2d.value : Rulesets(AccessRule.HIT), Stage.entrance_western_ee1.value : Rulesets(AccessRule.GLIDE, AccessRule.SHOOT), Stage.entrance_western_ed1.value : Rulesets(AccessRule.HIT), # Onsen Stage.entrance_onsen_aa1.value : Rulesets(AccessRule.HIT), Stage.entrance_onsen_aa2.value : Rulesets(AccessRule.HIT), Stage.entrance_onsen_a1a2.value : Rulesets(AccessRule.GLIDE, AccessRule.MAGICIAN), Stage.entrance_onsen_a2a1.value : Rulesets(AccessRule.GLIDE, AccessRule.MAGICIAN), Stage.entrance_onsen_a1a1m.value : Rulesets(AccessRule.GLIDE, AccessRule.MAGICIAN), Stage.entrance_onsen_a2a2m.value : Rulesets(AccessRule.GLIDE, AccessRule.MAGICIAN), Stage.entrance_onsen_b1b.value : Rulesets(AccessRule.GLIDE, [AccessRule.RCC, AccessRule.SWIM]), # Edotown Stage.entrance_edotown_a1a.value : Rulesets(AccessRule.NINJA, AccessRule.HERO), Stage.entrance_edotown_aa1.value : Rulesets(AccessRule.NINJA, AccessRule.HERO), Stage.entrance_edotown_ab1.value : Rulesets(AccessRule.NINJA, AccessRule.HERO), Stage.entrance_edotown_b1a.value : Rulesets(AccessRule.NINJA, AccessRule.HERO), Stage.entrance_edotown_b2b.value : Rulesets(AccessRule.NINJA, AccessRule.HERO), Stage.entrance_edotown_bb2.value : Rulesets(AccessRule.NINJA, AccessRule.HERO), Stage.entrance_edotown_bc1.value : Rulesets(AccessRule.HIT), Stage.entrance_edotown_c1c.value : Rulesets(AccessRule.NINJA, AccessRule.HERO), Stage.entrance_edotown_cc1.value : Rulesets(AccessRule.NINJA, AccessRule.HERO), Stage.entrance_edotown_cc2.value : Rulesets(AccessRule.HIT), # Heaven Stage.entrance_heaven_a1a.value : Rulesets(AccessRule.GLIDE), Stage.entrance_heaven_aa1.value : Rulesets(AccessRule.GLIDE), Stage.entrance_heaven_ab.value : Rulesets(AccessRule.GLIDE), Stage.entrance_heaven_bb1.value : Rulesets(AccessRule.HIT), # Toyhouse Stage.entrance_toyhouse_bb1.value : Rulesets(AccessRule.HIT), Stage.entrance_toyhouse_ef.value : Rulesets(AccessRule.HIT), Stage.entrance_toyhouse_fe.value : Rulesets(AccessRule.HIT), Stage.entrance_toyhouse_fa.value : Rulesets(AccessRule.HIT), # Iceland Stage.entrance_iceland_a1a.value : Rulesets(AccessRule.HIT), # Asia Stage.entrance_asia_a4d1.value : Rulesets(AccessRule.HIT), Stage.entrance_asia_bb2.value : Rulesets(event_invoked(Events.asia_b2_button.value)), Stage.entrance_asia_b2b.value : Rulesets(AccessRule.HIT), Stage.entrance_asia_ee1.value : Rulesets(AccessRule.DASH, AccessRule.RCC), # Plane Stage.entrance_plane_ac.value : Rulesets(AccessRule.RCC, AccessRule.DASH), Stage.entrance_plane_b1h.value : Rulesets(AccessRule.RCC, AccessRule.HERO), Stage.entrance_plane_hb1.value : Rulesets(AccessRule.RCC, AccessRule.HERO), Stage.entrance_plane_b1b2.value : Rulesets(AccessRule.RCC, AccessRule.HERO), Stage.entrance_plane_b2b1.value : Rulesets(AccessRule.RCC, AccessRule.HERO), Stage.entrance_plane_dd1.value : Rulesets(AccessRule.GLIDE), Stage.entrance_plane_d1d.value : Rulesets(AccessRule.GLIDE), Stage.entrance_plane_d1e.value : Rulesets(AccessRule.HIT), # Bay Stage.entrance_bay_cc1.value : Rulesets(AccessRule.HIT), Stage.entrance_bay_e2e3.value : Rulesets([AccessRule.FLY, AccessRule.SWIM, AccessRule.KUNGFU]), # Tomo Stage.entrance_tomo_aa1.value : Rulesets(AccessRule.HERO, AccessRule.NINJA), Stage.entrance_tomo_a1a.value : Rulesets(AccessRule.HERO, AccessRule.NINJA), Stage.entrance_tomo_ee1.value : Rulesets(AccessRule.KNIGHT), Stage.entrance_tomo_ee2.value : Rulesets(AccessRule.RCC), Stage.entrance_tomo_h1h.value : Rulesets(AccessRule.GLIDE), # Space Stage.entrance_space_ab.value : Rulesets(AccessRule.ATTACK), Stage.entrance_space_e1e_2.value : Rulesets([AccessRule.RCC, AccessRule.MORPH_NO_MONKEY]), }) class Casual(Normal): """ RuleType for a casual experience. The player is assumed to play the game without any or little advanced or obscure knowledge of it. """ def __init__(self): super().__init__() self.small_starting_channels = [6, 9, 11, 13, 15, 18, 20, 22, 23] self.blacklisted_entrances = [ Stage.entrance_asia_a1a2.value, Stage.entrance_bay_e1e3.value, Stage.entrance_bay_e3e4.value, Stage.entrance_tomo_f2f.value ] self.monkey_rules.update({ # Woods Loc.woods_salubon.value : Rulesets([AccessRule.RADAR, AccessRule.ATTACK]), Loc.woods_ukkilei.value : Rulesets(AccessRule.ATTACK), # Castle Loc.castle_pipo_guard.value : Rulesets(AccessRule.ATTACK), Loc.castle_monga.value : Rulesets(AccessRule.SHOOT), Loc.castle_ukkii.value : Rulesets(AccessRule.ATTACK), Loc.castle_sal_1000.value : Rulesets(AccessRule.ATTACK), # Ciscocity Loc.ciscocity_ukima.value : Rulesets(AccessRule.DASH), Loc.ciscocity_pipo_mondy.value : Rulesets([AccessRule.ATTACK, AccessRule.DASH]), # Studio Loc.studio_minoh.value : Rulesets(AccessRule.SHOOT), Loc.studio_monta.value : Rulesets(AccessRule.SHOOT), Loc.studio_monpii_ukkichi.value : Rulesets(AccessRule.ATTACK), # Halloween Loc.halloween_monkichiro.value : Rulesets(AccessRule.SWIM), Loc.halloween_uikkun.value : Rulesets(AccessRule.ATTACK), Loc.halloween_bonbon.value : Rulesets(AccessRule.ATTACK, event_invoked( Events.halloween_b_jumbo_robo.value), event_invoked( Events.halloween_b1_jumbo_robo_shoot.value)), Loc.halloween_chichi.value : Rulesets(AccessRule.ATTACK, event_invoked( Events.halloween_b_jumbo_robo.value), event_invoked( Events.halloween_b1_jumbo_robo_shoot.value)), Loc.halloween_ukkito.value : Rulesets(AccessRule.ATTACK), Loc.halloween_ukkiami.value : Rulesets(AccessRule.ATTACK), Loc.halloween_kabochin.value : Rulesets(AccessRule.ATTACK), Loc.halloween_mumpkin.value : Rulesets(AccessRule.ATTACK), # Western Loc.western_jay_mohn.value : Rulesets(AccessRule.ATTACK), Loc.western_jaja_jamo.value : Rulesets(AccessRule.ATTACK, AccessRule.HOOP), Loc.western_chammy_mo.value : Rulesets(AccessRule.SHOOT, AccessRule.FLY), Loc.western_golozo.value : Rulesets(AccessRule.ATTACK), Loc.western_golon_moe.value : Rulesets(AccessRule.MORPH_NO_MONKEY), # Onsen Loc.onsen_saru_sam.value : Rulesets(AccessRule.ATTACK), Loc.onsen_tome_san.value : Rulesets(AccessRule.ATTACK), Loc.onsen_domobeh.value : Rulesets(AccessRule.SWIM), Loc.onsen_kimi_san.value : Rulesets(AccessRule.ATTACK), Loc.onsen_mujakin.value : Rulesets(AccessRule.SWIM), # Snowfesta Loc.snowfesta_konkichi.value : Rulesets([AccessRule.ATTACK, AccessRule.RADAR]), Loc.snowfesta_pipotron_yellow.value : Rulesets(AccessRule.ATTACK), Loc.snowfesta_ukki_jii.value : Rulesets(AccessRule.ATTACK), # Edotown Loc.edotown_yosio.value : Rulesets(AccessRule.DASH), Loc.edotown_fatty_mcfats.value : Rulesets(AccessRule.ATTACK), Loc.edotown_golota.value : Rulesets(AccessRule.ATTACK), # Heaven Loc.heaven_ukkido.value : Rulesets(AccessRule.SHOOT), Loc.heaven_tami.value : Rulesets(AccessRule.ATTACK), Loc.heaven_kicchino.value : Rulesets(AccessRule.MORPH_NO_MONKEY), Loc.heaven_kimurin.value : Rulesets(AccessRule.MORPH_NO_MONKEY), Loc.heaven_valuccha.value : Rulesets(AccessRule.SHOOT), # Toyhouse Loc.toyhouse_monto.value : Rulesets(AccessRule.SHOOT), Loc.toyhouse_pipotron_red.value : Rulesets(AccessRule.MORPH_NO_MONKEY), Loc.toyhouse_golonero.value : Rulesets(AccessRule.ATTACK), # Iceland Loc.iceland_kushachin.value : Rulesets(AccessRule.ATTACK), Loc.iceland_malikko.value : Rulesets(AccessRule.ATTACK), Loc.iceland_bolikko.value : Rulesets(AccessRule.ATTACK), Loc.iceland_iceymon.value : Rulesets(AccessRule.ATTACK), # Arabian Loc.arabian_cup_o_mon.value : Rulesets(AccessRule.ATTACK), # Asia Loc.asia_baku.value : Rulesets([AccessRule.SHOOT, AccessRule.SWIM]), Loc.asia_takumon.value : Rulesets(AccessRule.SWIM), # Plane Loc.plane_temko.value : Rulesets(AccessRule.DASH), Loc.plane_pipotron_blue.value : Rulesets(AccessRule.MORPH_NO_MONKEY), Loc.plane_jeloh.value : Rulesets(AccessRule.ATTACK), Loc.plane_bongo.value : Rulesets(AccessRule.ATTACK), # Hong Loc.hong_uki_uki.value : Rulesets(AccessRule.SHOOT), Loc.hong_muki_muki.value : Rulesets(AccessRule.SHOOT), Loc.hong_bassili_ukki.value : Rulesets(AccessRule.DASH), Loc.hong_bankan.value : Rulesets(AccessRule.NINJA), Loc.hong_sukei.value : Rulesets(AccessRule.SHOOT), # Bay Loc.bay_nadamon.value : Rulesets(AccessRule.ATTACK), Loc.bay_nakabi.value : Rulesets(AccessRule.ATTACK), Loc.bay_gimi_gimi.value : Rulesets(AccessRule.ATTACK), Loc.bay_pokkini.value : Rulesets(AccessRule.ATTACK), # Tomo Loc.tomo_kichibeh.value : Rulesets(AccessRule.ATTACK), Loc.tomo_bonchicchi.value : Rulesets(AccessRule.ATTACK), Loc.tomo_mikibon.value : Rulesets(AccessRule.ATTACK), Loc.tomo_dj_tamo.value : Rulesets(AccessRule.DASH), Loc.tomo_chimpy.value : Rulesets(AccessRule.KUNGFU, AccessRule.NINJA), Loc.tomo_kajitan.value : Rulesets(AccessRule.KUNGFU, AccessRule.NINJA), Loc.tomo_uka_uka.value : Rulesets(AccessRule.KUNGFU, AccessRule.NINJA), Loc.tomo_mil_mil.value : Rulesets(AccessRule.KUNGFU, AccessRule.NINJA), Loc.tomo_goro_san.value : Rulesets(AccessRule.KNIGHT), Loc.tomo_tomio.value : Rulesets(AccessRule.ATTACK), Loc.tomo_gario.value : Rulesets(AccessRule.ATTACK), Loc.tomo_dj_pari.value : Rulesets(AccessRule.DASH), Loc.tomo_sal_13.value : Rulesets(AccessRule.ATTACK), Loc.tomo_sal_12.value : Rulesets(AccessRule.ATTACK), # Space Loc.space_miluchy.value : Rulesets(AccessRule.SHOOT, AccessRule.FLY), Loc.space_freet.value : Rulesets(AccessRule.ATTACK), Loc.space_chico.value : Rulesets(AccessRule.ATTACK), Loc.space_sal_10.value : Rulesets(AccessRule.ATTACK), Loc.space_sal_11.value : Rulesets(AccessRule.ATTACK), # Bosses Loc.boss_monkey_white.value : Rulesets(AccessRule.MORPH_NO_MONKEY), Loc.boss_monkey_blue.value : Rulesets(AccessRule.ATTACK), Loc.boss_monkey_yellow.value : Rulesets(AccessRule.NINJA), Loc.boss_monkey_pink.value : Rulesets(AccessRule.MORPH_NO_MONKEY), Loc.boss_monkey_red.value : Rulesets(AccessRule.MORPH_NO_MONKEY), Loc.boss_specter.value : Rulesets([AccessRule.MORPH_NO_MONKEY, AccessRule.SHOOT]), Loc.boss_specter_final.value : Rulesets([AccessRule.ATTACK, AccessRule.SWIM]) }) self.event_rules.update({ # Castle Events.castle_b_clapper.value : Rulesets(AccessRule.ATTACK), # Ciscocity Events.ciscocity_c_button.value : Rulesets(AccessRule.SHOOT, AccessRule.DASH, AccessRule.RCC), # Studio Events.studio_a1_button.value : Rulesets(AccessRule.ATTACK), Events.studio_a2_button.value : Rulesets(AccessRule.ATTACK), # Halloween Events.halloween_b_jumbo_robo.value : Rulesets(AccessRule.ATTACK), # Onsen Events.onsen_a_button.value : Rulesets(AccessRule.ATTACK), # Snowfesta Events.snowfesta_e_bell.value : Rulesets(AccessRule.ATTACK), # Edotown Events.edotown_b1_button.value : Rulesets(AccessRule.ATTACK), Events.edotown_e_scroll.value : Rulesets(AccessRule.CLUB, AccessRule.KNIGHT, AccessRule.NINJA, AccessRule.MAGICIAN, AccessRule.KUNGFU), # Heaven Events.heaven_b_clapper.value : Rulesets(AccessRule.ATTACK), # Iceland Events.iceland_c_jumbo_robo.value : Rulesets(AccessRule.ATTACK), # Asia Events.asia_b2_button.value : Rulesets(AccessRule.ATTACK), # Plane Events.plane_d1_clapper.value : Rulesets(AccessRule.ATTACK), # Hong Events.hong_b2_button.value : Rulesets(AccessRule.ATTACK), # Bay Events.bay_a7_button.value : Rulesets(AccessRule.ATTACK), # Space Events.space_g_button.value : Rulesets([AccessRule.SWIM, AccessRule.ATTACK]), }) self.entrance_rules.update({ # Seaside Stage.entrance_seaside_ab.value : Rulesets(AccessRule.ATTACK), # Woods Stage.entrance_woods_bc.value : Rulesets(AccessRule.ATTACK), # Castle Stage.entrance_castle_dd1.value : Rulesets(AccessRule.KNIGHT, AccessRule.SHOOT), Stage.entrance_castle_d1d.value : Rulesets(AccessRule.KNIGHT, AccessRule.SHOOT), Stage.entrance_castle_bb1.value : Rulesets(AccessRule.ATTACK), # Ciscocity Stage.entrance_ciscocity_ab.value : Rulesets(AccessRule.ATTACK), Stage.entrance_ciscocity_ce.value : Rulesets([AccessRule.MONKEY, AccessRule.ATTACK]), # Studio Stage.entrance_studio_d1d.value : Rulesets(AccessRule.ATTACK, AccessRule.GLIDE), Stage.entrance_studio_d2d.value : Rulesets(AccessRule.ATTACK), # Halloween Stage.entrance_halloween_a1a.value : Rulesets(AccessRule.SWIM), Stage.entrance_halloween_aa1.value : Rulesets(AccessRule.SWIM), Stage.entrance_halloween_c1c.value : Rulesets(AccessRule.SWIM), Stage.entrance_halloween_cc1.value : Rulesets(AccessRule.SWIM), Stage.entrance_halloween_cc2.value : Rulesets(AccessRule.SWIM), Stage.entrance_halloween_c2c.value : Rulesets(AccessRule.SWIM), Stage.entrance_halloween_dd1.value : Rulesets(AccessRule.KNIGHT), Stage.entrance_halloween_d1d.value : Rulesets(AccessRule.KNIGHT), Stage.entrance_halloween_d1d2.value : Rulesets(AccessRule.ATTACK), # Western Stage.entrance_western_bb1.value : Rulesets(AccessRule.ATTACK), Stage.entrance_western_fd2.value : Rulesets(AccessRule.ATTACK), Stage.entrance_western_dd2.value : Rulesets(AccessRule.ATTACK), Stage.entrance_western_d1d2.value : Rulesets(AccessRule.ATTACK), Stage.entrance_western_d2d.value : Rulesets(AccessRule.ATTACK), Stage.entrance_western_ed1.value : Rulesets(AccessRule.ATTACK), # Onsen Stage.entrance_onsen_aa1.value : Rulesets(AccessRule.ATTACK), Stage.entrance_onsen_aa2.value : Rulesets(AccessRule.ATTACK), Stage.entrance_onsen_a1a2.value : Rulesets(AccessRule.GLIDE), Stage.entrance_onsen_a2a1.value : Rulesets(AccessRule.GLIDE), Stage.entrance_onsen_a1a1m.value : Rulesets(AccessRule.GLIDE), Stage.entrance_onsen_a2a2m.value : Rulesets(AccessRule.GLIDE), Stage.entrance_onsen_b1b.value : Rulesets([AccessRule.RCC, AccessRule.SWIM]), Stage.entrance_onsen_bd1.value : Rulesets(AccessRule.SHOOT), Stage.entrance_onsen_dd1.value : Rulesets(AccessRule.RCC), # Edotown Stage.entrance_edotown_a1a.value : Rulesets(AccessRule.NINJA), Stage.entrance_edotown_aa1.value : Rulesets(AccessRule.NINJA), Stage.entrance_edotown_ab1.value : Rulesets(AccessRule.GLIDE), Stage.entrance_edotown_b1a.value : Rulesets(AccessRule.GLIDE), Stage.entrance_edotown_b2b1.value : Rulesets([event_invoked(Events.edotown_b1_button.value), AccessRule.NINJA], AccessRule.SWIM), Stage.entrance_edotown_b2b.value : Rulesets(AccessRule.NINJA), Stage.entrance_edotown_bb2.value : Rulesets(AccessRule.NINJA), Stage.entrance_edotown_bc1.value : Rulesets(AccessRule.ATTACK), Stage.entrance_edotown_c1c.value : Rulesets(AccessRule.NINJA), Stage.entrance_edotown_cc1.value : Rulesets(AccessRule.NINJA), Stage.entrance_edotown_cc2.value : Rulesets(AccessRule.ATTACK), # Heaven Stage.entrance_heaven_a1a.value : Rulesets(AccessRule.FLY), Stage.entrance_heaven_aa1.value : Rulesets(AccessRule.FLY), Stage.entrance_heaven_ab.value : Rulesets(AccessRule.NINJA, AccessRule.HERO, [AccessRule.FLYER, AccessRule.HOOP]), Stage.entrance_heaven_bb1.value : Rulesets(AccessRule.ATTACK), # Toyhouse Stage.entrance_toyhouse_bb1.value : Rulesets(AccessRule.ATTACK), Stage.entrance_toyhouse_ef.value : Rulesets(AccessRule.ATTACK), Stage.entrance_toyhouse_fe.value : Rulesets(AccessRule.ATTACK), Stage.entrance_toyhouse_fa.value : Rulesets(AccessRule.ATTACK), # Iceland Stage.entrance_iceland_a1a.value : Rulesets(AccessRule.ATTACK), Stage.entrance_iceland_aa2.value : Rulesets(AccessRule.GLIDE), Stage.entrance_iceland_a2a.value : Rulesets(AccessRule.GLIDE), Stage.entrance_iceland_be.value : Rulesets(AccessRule.DASH), # Asia Stage.entrance_asia_ab.value : Rulesets(AccessRule.SWIM), Stage.entrance_asia_ba.value : Rulesets(AccessRule.SWIM), Stage.entrance_asia_a1a.value : Rulesets(AccessRule.SWIM), Stage.entrance_asia_a1b2.value : Rulesets(AccessRule.SWIM), Stage.entrance_asia_a1a3.value : Rulesets(AccessRule.SWIM), Stage.entrance_asia_a2a1.value : Rulesets(AccessRule.SWIM), Stage.entrance_asia_a2a3.value : Rulesets([event_invoked(Events.asia_a_block.value), event_invoked(Events.asia_a1_block.value), event_invoked(Events.asia_a2_block.value)]), Stage.entrance_asia_a2a4.value : Rulesets(AccessRule.SWIM), Stage.entrance_asia_a3a1.value : Rulesets(AccessRule.SWIM), Stage.entrance_asia_a3a4.value : Rulesets(AccessRule.SWIM), Stage.entrance_asia_a3a2.value : Rulesets([event_invoked(Events.asia_a_block.value), event_invoked(Events.asia_a1_block.value), event_invoked(Events.asia_a2_block.value)]), Stage.entrance_asia_a4a3.value : Rulesets(AccessRule.SWIM), Stage.entrance_asia_a4d1.value : Rulesets(AccessRule.ATTACK), Stage.entrance_asia_b2b.value : Rulesets(AccessRule.ATTACK), Stage.entrance_asia_dd1.value : Rulesets([AccessRule.SWIM, AccessRule.FLY]), Stage.entrance_asia_ee1.value : Rulesets([AccessRule.ATTACK, AccessRule.DASH, AccessRule.RCC]), # Plane Stage.entrance_plane_dd1.value : Rulesets(AccessRule.FLY), Stage.entrance_plane_d1d.value : Rulesets(AccessRule.FLY), Stage.entrance_plane_d1e.value : Rulesets(AccessRule.ATTACK), # Hong Stage.entrance_hong_aa1.value : Rulesets(AccessRule.KUNGFU), Stage.entrance_hong_a1a2.value : Rulesets(AccessRule.ATTACK), Stage.entrance_hong_cc1.value : Rulesets([AccessRule.ATTACK, AccessRule.GLIDE]), Stage.entrance_hong_c1c.value : Rulesets([AccessRule.ATTACK, AccessRule.GLIDE]), # Bay Stage.entrance_bay_aa1.value : Rulesets([AccessRule.ATTACK, AccessRule.SHOOT, AccessRule.SWIM]), Stage.entrance_bay_a1a.value : Rulesets([AccessRule.ATTACK, AccessRule.SWIM]), Stage.entrance_bay_a3a4.value : Rulesets(AccessRule.RCC), Stage.entrance_bay_cc1.value : Rulesets([AccessRule.ATTACK, AccessRule.FLY]), Stage.entrance_bay_e1e2.value : Rulesets(AccessRule.ATTACK), # Tomo Stage.entrance_tomo_bc.value : Rulesets(AccessRule.GLIDE), # Space Stage.entrance_space_ff1.value : Rulesets(AccessRule.GLIDE), Stage.entrance_space_gg1.value : Rulesets([AccessRule.SWIM, AccessRule.ATTACK]), }) LogicPreferenceOptions : list = [ Casual, Normal, Hard, Expert ] # [<--- SHOP ITEMS RULES HANDLING --->] @dataclass class ShopItemRules: item_rules : dict[str, Rulesets] entrances : list[AE3EntranceMeta] entrance_rules : dict[str, Rulesets] post_game_items: set[str] post_game_entrances: set[str] blacklisted_items: set[str] blacklisted_entrances : list[str] blacklisted_stages : list[str] cheap_early_items : list[str] sets : int = 0 def __init__(self, world : "AE3World"): self.item_rules = {} self.entrances = [] self.entrance_rules = {} self.post_game_items = set() self.post_game_entrances = set() self.blacklisted_items = set() self.blacklisted_entrances = [*ENTRANCES_SHOP_PROGRESSION] self.blacklisted_stages = [*STAGES_SHOP_PROGRESSION] self.cheap_early_items = [] self.sets = 0 if not world.options.shoppingsanity: return # If farmable areas are not available before Post Game, and Cheap Items have a minimum requirement of PGC, # All Shop Items will only be in logic after Post Game is unlocked. # This also removes any Shop Item from being a requirement for PGC are_farmables_available: bool = ShopItemRules.are_farmables_available(world) if not are_farmables_available and world.options.cheap_items_minimum_requirement >= 100: self.post_game_entrances.add(Stage.entrance_travel_ab.value) self.post_game_items.update(SHOP_PERSISTENT_MASTER) if world.options.shoppingsanity == 2: self.post_game_items.update(SHOP_COLLECTION_MASTER) else: self.post_game_items.update(SHOP_UNIQUE_MASTER) if world.options.shoppingsanity >= 3: self.blacklisted_entrances = [] self.blacklisted_stages = [] return # Post Game Properties required_keys : int = len(world.progression.progression) - 3 can_farm : list[Rulesets] = [AccessRule.FARM] if world.options.farm_logic_sneaky_borgs.value: can_farm.append(AccessRule.FARM_DUPE) self.item_rules = { Loc.hint_book_9.value: Rulesets(can_access_region(Stage.region_heaven_a.value)), Loc.hint_book_14.value: Rulesets(can_access_region(Stage.region_boss1.value)), Loc.hint_book_15.value: Rulesets(can_access_region(Stage.region_boss2.value)), Loc.hint_book_16.value: Rulesets(can_access_region(Stage.region_boss3.value)), Loc.hint_book_17.value: Rulesets(can_access_region(Stage.region_boss4.value)), Loc.hint_book_18.value: Rulesets(can_access_region(Stage.region_boss5.value)), Loc.hint_book_19.value: Rulesets(can_access_region(Stage.region_boss6.value)), Loc.hint_book_20.value: Rulesets(can_access_region(Stage.region_specter1.value)), Loc.movie_tape_17.value: Rulesets(can_access_region(Stage.region_tomo_a.value)), Loc.movie_tape_26.value: Rulesets(can_access_region(Stage.region_specter2.value)), Loc.movie_tape_27.value: Rulesets(can_access_region(Stage.region_specter2.value)), Loc.music_disc_41.value: Rulesets(can_access_region(Stage.region_specter2.value)), } self.post_game_items.add(Loc.shop_ultim_ape_fighter.value) self.entrance_rules = { Stage.entrance_shop_expensive.value : Rulesets(*can_farm), Stage.entrance_shop_morph.value : Rulesets(AccessRule.MORPH), } if world.options.shoppingsanity.value <= 1: return post_game_start_index: int = sum(world.progression.progression[:-1]) post_game_end_index: int = post_game_start_index + world.progression.progression[-2] post_game_channels: list[str] = [LEVELS_BY_ORDER[channel] for channel in world.progression.order[post_game_start_index:post_game_end_index]] post_game_areas: list[str] = [] for channel in post_game_channels: post_game_areas.extend(STAGES_DIRECTORY_LABEL[channel]) for channel, items in SHOP_EVENT_ACCESS_DIRECTORY.items(): if channel in post_game_areas: self.post_game_items.update(items) cheap_items_early_amount: int = world.options.cheap_items_early_amount.value if world.options.cheap_items_minimum_requirement: self.entrance_rules[Stage.entrance_travel_ab.value] = Rulesets(*can_farm) cheap_items_early_amount = 0 if world.options.cheap_items_minimum_requirement.value < 100: cheap_items_rule = has_keys(math.ceil( required_keys * (world.options.cheap_items_minimum_requirement.value / 100))) self.entrance_rules[Stage.entrance_travel_ab.value].update(Rulesets(cheap_items_rule)) else: if world.options.shoppingsanity.value != 2: self.post_game_items.update(SHOP_CHEAP_MASTER) else: self.post_game_items.update(SHOP_CHEAP_COLLECTION_MASTER) if world.options.shoppingsanity.value == 2: if cheap_items_early_amount: for category in SHOP_CHEAP_COLLECTION_INDEX: self.cheap_early_items.extend(category[:cheap_items_early_amount]) return # Progression Splitting Properties elif world.options.shoppingsanity.value == 3: self.sets = math.ceil(28 / (required_keys + 2)) reached_shop_progress = lambda amount : has_keys(amount) self.blacklisted_entrances.clear() self.blacklisted_stages.clear() else: self.sets : int = math.ceil(28 / world.options.restock_progression.value) reached_shop_progress = lambda amount: has_shop_stock(amount) self.blacklisted_entrances.clear() self.blacklisted_stages.clear() enough_cheap_items : bool = False for i, entrance in enumerate(ENTRANCES_SHOP_PSEUDOREGIONS): current_set = math.floor(i / self.sets) if world.options.shoppingsanity.value == 3 and current_set * self.sets + self.sets >= 27: self.post_game_entrances.add(entrance.name) if math.floor(current_set / self.sets) > 0: self.entrance_rules[entrance.name] = Rulesets(reached_shop_progress(math.floor(i / self.sets))) if cheap_items_early_amount and not enough_cheap_items: cheap : list[str] = [item for item in SHOP_PROGRESSION_DIRECTORY[entrance.destination] if item in SHOP_CHEAP_MASTER] expensive : list[str] = [item for item in SHOP_PROGRESSION_DIRECTORY[entrance.destination] if item not in SHOP_CHEAP_MASTER] if len(self.cheap_early_items) + len(cheap) <= cheap_items_early_amount: self.cheap_early_items.extend(cheap) if are_farmables_available: for items in expensive: self.item_rules[items] = Rulesets(*can_farm) else: self.post_game_items.update(expensive) self.entrances.append(AE3EntranceMeta(entrance.name, Stage.travel_station_b.value, entrance.destination)) else: enough_cheap_items = True self.entrances.append(AE3EntranceMeta(entrance.name, Stage.region_shop_expensive.value, entrance.destination)) else: self.entrances.append(AE3EntranceMeta(entrance.name, Stage.region_shop_expensive.value, entrance.destination)) def set_pgc_rules(self, world : "AE3World") -> None: if not world.options.shoppingsanity.value: return required_keys:int = len(world.progression.progression) - 3 post_game_condition_rule:Callable[[CollectionState, int], bool] = world.post_game_condition.enact( required_keys - 1, world.options.monkeysanity_break_rooms.value) self.item_rules[Loc.shop_ultim_ape_fighter.value] = Rulesets(post_game_condition_rule) if world.options.cheap_items_minimum_requirement.value >= 100: self.entrance_rules.setdefault( Stage.entrance_travel_ab.value, Rulesets()).update(Rulesets(post_game_condition_rule)) for item in self.post_game_items: self.item_rules[item] = Rulesets(post_game_condition_rule) for entrance in self.post_game_entrances: self.entrance_rules[entrance] = Rulesets(post_game_condition_rule) @staticmethod def are_farmables_available(world: 'AE3World') -> bool: if not world.options.shoppingsanity.value: return True farming_areas: list[str] = [*STAGES_FARMABLE] if world.options.farm_logic_sneaky_borgs: farming_areas.extend([*STAGES_FARMABLE_SNEAKY_BORG]) post_game_start_index: int = sum(world.progression.progression[:-1]) post_game_end_index: int = post_game_start_index + world.progression.progression[-2] post_game_channels: list[str] = [LEVELS_BY_ORDER[channel] for channel in world.progression.order[post_game_start_index:post_game_end_index]] post_game_areas: list[str] = [] for channel in post_game_channels: post_game_areas.extend(STAGES_DIRECTORY_LABEL[channel]) return any(stage not in post_game_areas for stage in farming_areas) # [<--- GOAL TARGETS --->] class Specter(GoalTarget): name = "Specter" description = "Capture Specter by clearing \"Specter Battle!\"" locations = {Loc.boss_specter.value} class SpecterFinal(Specter): name = "Specter Final" description = "Capture Specter by clearing \"Specter's Final Battle!\"" locations = {Loc.boss_specter_final.value} class TripleThreat(GoalTarget): name = "Triple Threat" description = "Defeat at least 3 bosses!" locations = {*MONKEYS_BOSSES} amount = 3 class PlaySpike(GoalTarget): name = "Play Spike" description = "Go and Capture 204 Pipo Monkeys!" locations = {monkey for monkey in MONKEYS_MASTER if monkey != Loc.boss_tomoki.value and monkey not in MONKEYS_PASSWORDS} amount = 204 def verify(self, state: CollectionState, player: int) -> bool: minimum_equipment : list[Callable] = [AccessRule.DASH, AccessRule.SWIM, AccessRule.SLING, AccessRule.RCC, AccessRule.MAGICIAN, AccessRule.KUNGFU, AccessRule.HERO] if any(monkey in MONKEYS_BREAK_ROOMS for monkey in self.locations): minimum_equipment.append(AccessRule.MONKEY) for rule in minimum_equipment: if not rule(state, player): return False return True class PlayJimmy(PlaySpike): name = "Play Jimmy" description = "Go and Capture 300 Pipo Monkeys!" amount = 300 class DirectorsCut(GoalTarget): name = "Director's Cut" description = "Capture all Monkey Films across all the channels!" locations = {*CAMERAS_MASTER} class PhoneCheck(DirectorsCut): name = "Phone Check" description = "Activate all Cellphones scattered across the channels!" locations = {*Cellphone_Name_to_ID.values()} class ShopCollector(GoalTarget): name = "Shop Collector" description = "Buy all of the Shop Items in the Shopping Area!" locations = {*LOCATIONS_DIRECTORY[APHelper.shop.value]} amount = len(SHOP_UNIQUE_MASTER) class PasswordHunt(DirectorsCut): name = "Password Hunt" locations = {*MONKEYS_PASSWORDS} GoalTargetOptions : list[Callable] = [ Specter, SpecterFinal, TripleThreat, PlaySpike, PlayJimmy, DirectorsCut, PhoneCheck, ShopCollector ]