Files
dockipelago/worlds/apeescape3/data/Rules.py
Jonathan Tinney 7971961166
Some checks failed
Analyze modified files / flake8 (push) Failing after 2m28s
Build / build-win (push) Has been cancelled
Build / build-ubuntu2204 (push) Has been cancelled
ctest / Test C++ ubuntu-latest (push) Has been cancelled
ctest / Test C++ windows-latest (push) Has been cancelled
Analyze modified files / mypy (push) Has been cancelled
Build and Publish Docker Images / Push Docker image to Docker Hub (push) Successful in 5m4s
Native Code Static Analysis / scan-build (push) Failing after 5m2s
type check / pyright (push) Successful in 1m7s
unittests / Test Python 3.11.2 ubuntu-latest (push) Failing after 16m23s
unittests / Test Python 3.12 ubuntu-latest (push) Failing after 28m19s
unittests / Test Python 3.13 ubuntu-latest (push) Failing after 14m49s
unittests / Test hosting with 3.13 on ubuntu-latest (push) Successful in 5m0s
unittests / Test Python 3.13 macos-latest (push) Has been cancelled
unittests / Test Python 3.11 windows-latest (push) Has been cancelled
unittests / Test Python 3.13 windows-latest (push) Has been cancelled
add schedule I, sonic 1/frontiers/heroes, spirit island
2026-04-02 23:46:36 -07:00

2136 lines
105 KiB
Python

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
]