diff --git a/worlds/am2r/Client.py b/worlds/am2r/Client.py new file mode 100644 index 000000000000..0eea8c0b6ac5 --- /dev/null +++ b/worlds/am2r/Client.py @@ -0,0 +1,231 @@ +import asyncio +import copy +import json +import time +import random +from asyncio import StreamReader, StreamWriter +from typing import List +from worlds.am2r.items import item_table +from worlds.am2r.locations import get_location_datas + +import Utils +from Utils import async_start +from CommonClient import CommonContext, server_loop, gui_enabled, ClientCommandProcessor, logger, \ + get_base_parser + +CONNECTION_TIMING_OUT_STATUS = "Connection timing out" +CONNECTION_REFUSED_STATUS = "Connection Refused" +CONNECTION_RESET_STATUS = "Connection was reset" +CONNECTION_TENTATIVE_STATUS = "Initial Connection Made" +CONNECTION_CONNECTED_STATUS = "Connected" +CONNECTION_INITIAL_STATUS = "Connection has not been initiated" +item_location_scouts = {} +item_id_to_game_id: dict = {item.code: item.game_id for item in item_table.values()} +location_id_to_game_id: dict = {location.code: location.game_id for location in get_location_datas(None, None)} +game_id_to_location_id: dict = {location.game_id: location.code for location in get_location_datas(None, None) if location.code != None} + + + +class AM2RCommandProcessor(ClientCommandProcessor): + def __init__(self, ctx: CommonContext): + super().__init__(ctx) + + def _cmd_am2r(self): + """Check AM2R Connection State""" + if isinstance(self.ctx, AM2RContext): + logger.info(f"Connection Status: {self.ctx.am2r_status}") + + def _cmd_septoggs(self): + """Septogg information""" + logger.info("Hi, messenger for the co-creator of the Septoggs here. The Septoggs were creatures found in the \ +original MII as platforms to help samus with Space Jumping, we wanted to include them along with the Blob Throwers to \ +complete the enemy roster from the original game, but had to come up with another purpose for them to work besides \ +floating platforms. They do help the player, which is most noticeable in randomizer modes, but they also act as \ +environmental story telling, akin to the Zebesian Roaches and Tatori from Super Metroid. This can be seen with the Baby \ +Septoggs randomly appearing in certain areas with camouflage of that environment, more and more babies appearing by \ +Metroid husks in the breeding grounds after more Metroids are killed in the area (to show how much damage the Metroids \ +can cause to the ecosystem and establish that Septoggs are scavengers), and Baby Septoggs staying close to Elder \ +Septoggs (as they feel safe next to the durable Elders)") + + def _cmd_credits(self): + """Huge thanks to all the people listed here""" + logger.info("AM2R Multiworld Randomizer brought to you by:") + logger.info("Programmers: Ehseezed DodoBirb") + logger.info("Sprite Artists: Abyssal Creature, Mimolette") + logger.info("Special Thanks to all the beta testers and the AM2R Community Updates Team") + logger.info("And Variable who was conned into becoming a programmer to fix issues he found") + + +class AM2RContext(CommonContext): + command_processor = AM2RCommandProcessor + game = 'AM2R' + items_handling = 0b111 # full remote + + def __init__(self, server_address, password): + super().__init__(server_address, password) + self.waiting_for_client = False + self.am2r_streams: (StreamReader, StreamWriter) = None + self.am2r_sync_task = None + self.am2r_status = CONNECTION_INITIAL_STATUS + self.received_locscouts = False + self.metroids_required = 41 + self.client_requesting_scouts = False + + async def server_auth(self, password_requested: bool = False): + if password_requested and not self.password: + await super().server_auth(password_requested) + if not self.auth: + self.waiting_for_client = True + logger.info('No AM2R details found. Reconnect to MW server after AM2R is connected.') + return + + await self.send_connect() + + def run_gui(self): + from kvui import GameManager + + class AM2RManager(GameManager): + logging_pairs = [ + ("Client", "Archipelago") + ] + base_title = "AM2R Multiworld Client" + + self.ui = AM2RManager(self) + self.ui_task = asyncio.create_task(self.ui.async_run(), name="UI") + + def on_package(self, cmd: str, args: dict): + if cmd == "Connected": + self.metroids_required = args["slot_data"]["MetroidsRequired"] + elif cmd == "LocationInfo": + logger.info("Received Location Info") + +def get_payload(ctx: AM2RContext): + items_to_give = [item_id_to_game_id[item.item] for item in ctx.items_received if item.item in item_id_to_game_id] + if not ctx.locations_info: + locations = [location.code for location in get_location_datas(None, None) if location.code is not None] + async_start(ctx.send_msgs([{"cmd": "LocationScouts", "locations": locations, "create_as_hint": 0}])) + return json.dumps({ + "cmd": "items", "items": items_to_give + }) + + if ctx.client_requesting_scouts: + itemdict = {} + for locationid, netitem in ctx.locations_info.items(): + gamelocation = location_id_to_game_id[locationid] + if netitem.item in item_id_to_game_id: + if netitem.flags & 0b100 != 0: + gameitem = random.randint(0, 20) + else: + gameitem = item_id_to_game_id[netitem.item] + else: + gameitem = 20 + itemdict[gamelocation] = gameitem + print("Sending") + return json.dumps({ + 'cmd':"locations", 'items': itemdict, 'metroids': ctx.metroids_required + }) + return json.dumps({ + "cmd": "items", "items": items_to_give + }) + +async def parse_payload(ctx: AM2RContext, data_decoded): + item_list = [game_id_to_location_id[int(location)] for location in data_decoded["Items"]] + game_finished = bool(int(data_decoded["GameCompleted"])) + item_set = set(item_list) + ctx.locations_checked = item_list + new_locations = [location for location in ctx.missing_locations if location in item_set] + if new_locations: + await ctx.send_msgs([{"cmd": "LocationChecks", "locations": new_locations}]) + if game_finished and not ctx.finished_game: + await ctx.send_msgs([{"cmd": "StatusUpdate", "status": 30}]) + ctx.finished_game = True + +async def am2r_sync_task(ctx: AM2RContext): + logger.info("Starting AM2R connector, use /am2r for status information.") + while not ctx.exit_event.is_set(): + error_status = None + if ctx.am2r_streams: + (reader, writer) = ctx.am2r_streams + msg = get_payload(ctx).encode() + writer.write(msg) + writer.write(b'\n') + try: + await asyncio.wait_for(writer.drain(), timeout=1.5) + try: + data = await asyncio.wait_for(reader.readline(), timeout=5) + data_decoded = json.loads(data.decode()) + ctx.auth = data_decoded["SlotName"] + ctx.password = data_decoded["SlotPass"] + ctx.client_requesting_scouts = not bool(int(data_decoded["SeedReceived"])) + await parse_payload(ctx, data_decoded) + except asyncio.TimeoutError: + logger.debug("Read Timed Out, Reconnecting") + error_status = CONNECTION_TIMING_OUT_STATUS + writer.close() + ctx.am2r_streams = None + except ConnectionResetError as e: + logger.debug("Read failed due to Connection Lost, Reconnecting") + error_status = CONNECTION_RESET_STATUS + writer.close() + ctx.am2r_streams = None + except TimeoutError: + logger.debug("Connection Timed Out, Reconnecting") + error_status = CONNECTION_TIMING_OUT_STATUS + writer.close() + ctx.am2r_streams = None + except ConnectionResetError: + logger.debug("Connection Lost, Reconnecting") + error_status = CONNECTION_RESET_STATUS + writer.close() + ctx.am2r_streams = None + + if ctx.am2r_status == CONNECTION_TENTATIVE_STATUS: + if not error_status: + logger.info("Successfully Connected to AM2R") + ctx.am2r_status = CONNECTION_CONNECTED_STATUS + else: + ctx.am2r_status = f"Was tentatively connected but error occured: {error_status}" + elif error_status: + ctx.am2r_status = error_status + logger.info("Lost connection to AM2R and attempting to reconnect. Use /am2r for status updates") + else: + try: + logger.debug("Attempting to connect to AM2R") + ctx.am2r_streams = await asyncio.wait_for(asyncio.open_connection("127.0.0.1", 64197), timeout=10) + ctx.am2r_status = CONNECTION_TENTATIVE_STATUS + except TimeoutError: + logger.debug("Connection Timed Out, Trying Again") + ctx.am2r_status = CONNECTION_TIMING_OUT_STATUS + continue + except ConnectionRefusedError: + logger.debug("Connection Refused, Trying Again") + ctx.am2r_status = CONNECTION_REFUSED_STATUS + continue + + +def launch(): + # Text Mode to use !hint and such with games that have no text entry + Utils.init_logging("AM2RClient") + + options = Utils.get_options() + + async def main(args): + random.seed() + ctx = AM2RContext(args.connect, args.password) + ctx.server_task = asyncio.create_task(server_loop(ctx), name="ServerLoop") + if gui_enabled: + ctx.run_gui() + ctx.run_cli() + ctx.am2r_sync_task = asyncio.create_task(am2r_sync_task(ctx), name="AM2R Sync") + await ctx.exit_event.wait() + ctx.server_address = None + + await ctx.shutdown() + + import colorama + + parser = get_base_parser() + args = parser.parse_args() + colorama.init() + asyncio.run(main(args)) + colorama.deinit() \ No newline at end of file diff --git a/worlds/am2r/__init__.py b/worlds/am2r/__init__.py new file mode 100644 index 000000000000..2a480843a03a --- /dev/null +++ b/worlds/am2r/__init__.py @@ -0,0 +1,111 @@ +import types +from typing import Dict +from .items import item_table +from .locations import get_location_datas, EventId +from .regions import create_regions_and_locations +from BaseClasses import Tutorial, Item +from .options import AM2R_options, MetroidsAreChecks +from worlds.AutoWorld import World, WebWorld +from worlds.LauncherComponents import Component, components, Type, launch_subprocess + + +def launch_client(): + from .Client import launch + launch_subprocess(launch, name="AM2RClient") + + +components.append(Component("AM2R Client", "AM2RClient", func=launch_client, component_type=Type.CLIENT)) + + +class AM2RWeb(WebWorld): + theme = "partyTime" + tutorials = [Tutorial( + "Multiworld Setup Guide", + "A guide to setting up the Archipelago AM2R software on your computer. This guide covers single-player, multiworld, and related software.", + "English", + "setup_en.md", + "setup/en", + ["Zed"] + )] + + +class AM2RWorld(World): + """ + AM2R is a remake of the classic Metroid 2 game for the Game Boy that tries its best to keep the feel + of the original as well as filling in some gaps to more closely tie into Metroid Fusion and brings some + items from there as well. + """ + game = "AM2R" + option_definitions = options.AM2R_options + web = AM2RWeb() + + item_name_to_id = items.item_name_to_id + location_name_to_id = {location.name: location.code for location in get_location_datas(None, None)} + + item_name_groups = items.item_name_groups + data_version = 1 + + def fill_slot_data(self) -> Dict[str, object]: + return {name: getattr(self.multiworld, name)[self.player].value for name in self.option_definitions} + + def create_regions(self) -> None: + create_regions_and_locations(self.multiworld, self.player) + + def create_item(self, name: str) -> Item: + return items.create_item(self.player, name) + + def create_items(self) -> None: + if self.options.MetroidsAreChecks != MetroidsAreChecks.option_include_A6: + self.multiworld.get_location("Deep Caves: Lil\' Bro", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Deep Caves: Big Sis", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Omega Nest: SA-X Queen Lucina", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Omega Nest: Epsilon", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Omega Nest: Druid", self.player).place_locked_item(self.create_item("Metroid")) + if self.options.MetroidsAreChecks == MetroidsAreChecks.option_disabled: + self.multiworld.get_location("The Forgotten Alpha", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Golden Temple: Friendly Spider", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Golden Temple Nest: Moe", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Golden Temple Nest: Larry", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Golden Temple Nest: Curly", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Main Caves: Freddy Fazbear", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Hydro Station: Turbine Terror", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Hydro Station: The Lookout", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Hydro Station: Recent Guardian", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Hydro Nest: EnderMahan", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Hydro Nest: Carnage Awful", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Hydro Nest: Venom Awesome", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Hydro Nest: Something More, Something Awesome",self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Industrial Nest: Mimolette", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Industrial Nest: The Big Cheese", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Industrial Nest: Mohwir", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Industrial Nest: Chirn", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Industrial Nest: BHHarbinger", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Industrial Nest: The Abyssal Creature", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Industrial Complex: Sisyphus", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Industrial Complex: And then there\'s this Asshole",self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Inside Industrial: Guardian of Doom Treadmill",self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Inside Industrial: Rawsome1234 by the Lava Lake",self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Dual Alphas: Marco", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Dual Alphas: Polo", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Mines: Unga", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Mines: Gunga", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("The Tower: Patricia", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("The Tower: Variable \"GUH\"", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Ruler of The Tower: Slagathor", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("The Tower: Mr.Sandman", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("The Tower: Anakin", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("The Tower: Xander", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("EMP: Sir Zeta Commander of the Alpha Squadron", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Alpha Squadron: Timmy", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Alpha Squadron: Tommy", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Alpha Squadron: Terry", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Alpha Squadron: Telly", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Alpha Squadron: Martin", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Underwater: Gamma Bros Mario", self.player).place_locked_item(self.create_item("Metroid")) + self.multiworld.get_location("Underwater: Gamma Bros Luigi", self.player).place_locked_item(self.create_item("Metroid")) + + self.multiworld.get_location("The Last Metroid is in Captivity", self.player).place_locked_item(self.create_item("The Galaxy is at Peace")) + items.create_all_items(self.multiworld, self.player) + + def set_rules(self) -> None: + self.multiworld.completion_condition[self.player] = lambda state: state.has("The Galaxy is at Peace", self.player) diff --git a/worlds/am2r/docs/en_AM2R.md b/worlds/am2r/docs/en_AM2R.md new file mode 100644 index 000000000000..44cc4cc05704 --- /dev/null +++ b/worlds/am2r/docs/en_AM2R.md @@ -0,0 +1,23 @@ +# AM2R Multiworld Beta (Text Adventure) +## How to use +There currently is no client to use this with + + +Download and install the latest version of [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases) the `AM2R.apworld` from below. +Once you have installed Archipelago you can take the `AM2R.apworld` and put it inside the `/lib/worlds` portion of your Archipelago install. +From there to get a generic `AM2R.yaml` open the Archipelago Launcher and click the **Generate Template settings** which will add it to the template yaml folder in `/Players/Templates`. +### Things are subject to change at developer discretion +### Seeds should be beatable but its possible there are still logical errors +## Bug Reporting +If you are playing this mod and are encountering bugs **DO NOT** report them to the Archipelago server this is not offical support from them, and they may or may not know what to do instead you can report them to the dedicated mod thread on the [AM2R Server](https://discord.gg/YTQnkAJ) or on the [Modding Server](https://discord.gg/Fdq3MSXEDb) and you can always open an issue on the github. +### Help I found a problem with 100% trap fill playing on fusion mode +Fuck you Abyss said it was possible, and I was there. + +## Randomizer Information + +### What is randomized? +All items both majors and minors are randomized together in one pool with the option to add in the metroids as checks as well. +### What is the goal? +The goal of this AM2R randomizer implementation is to kill or collect the self specified number of metroids to reach the queen metroid and save the baby metorid +### What do I get with 100% trap fill? +With max trap fill you get 1 copy of every major as well as 2 PB packs and 1 Super Missile diff --git a/worlds/am2r/docs/setup_en.md b/worlds/am2r/docs/setup_en.md new file mode 100644 index 000000000000..abbd9b994aaf --- /dev/null +++ b/worlds/am2r/docs/setup_en.md @@ -0,0 +1,104 @@ +# AM2R Archipelago Setup Guide + +## Required Software +- [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases/latest) +- [AM2R Multiworld Mod](https://github.com/DodoBirby/AM2R-Multiworld-Mod/releases/latest) +- [AM2R Launcher](https://github.com/AM2R-Community-Developers/AM2RLauncher/releases/latest) +- Your self provided copy of AM2R 1.1 for the launcher + +## Installation Process + +### Installing Multiworld Mod + +Download and set up the latest release of [AM2R Launcher](https://github.com/AM2R-Community-Developers/AM2RLauncher/releases) using your copy of AM2R 1.1 then in the launcher you can +under the `Mod Settings` tab, hit the `ADD NEW MOD` button, and select Multiworld mod `.zip` file from [AM2R Multiworld Mod](https://github.com/DodoBirby/AM2R-Multiworld-Mod/releases/latest) +finally from the `Play` tab select the `Multiworld` profile and hit `Install` and then play to load up the mod. + + +### Installing `am2r.apworld` + +After downloading the `am2r.apworld` file you will need to move it to your standard 0.4.4 or newer installation of +[Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases/latest), +you will need to navigate to the `/lib/worlds` folder of the Archipelago installation and add the `am2r.apworld` file into there. + +From there you can use the `Archipelago Launcher` to find and open `AM2R Client` in the future steps. + +## Hosting a Multiworld Session + +After collecting all the players `.yaml` files and placing them into the `/players` folder you can run `Archipelago Generate` +at which point it may ask you for roms if needed. If you do not have the roms you cannot generate the seed, and you will +need somebody else with all the roms to generate the seed for the group. + +After the generation is complete there will be a `.zip` file in the `/output` folder of the Archipelago installation +that you can then upload directly to the [Archipelago Website](https://archipelago.gg/uploads) to host the session, or +you can utilize the `Archipelago Server` to host the session yourself. + +## Joining a Multiworld Session + +### Creating a `.yaml` file + +In the `Archipelago Launcher` hit the `Generate Template Settings` button which will generate a default `.yaml` file +for all your currently installed worlds, this will add a `am2r.yaml` file to the `/players/templates` folder of the +Archipelago installation. + +The `.yaml` file is a special text file that can be edited in any text editor including your OS's default text editor. +I would recommend installing at least [Sublime Text](https://www.sublimetext.com/) or [Notepad++](https://notepad-plus-plus.org/downloads/) for the easiest experience. + +The `.yaml` file will look something like this: +```yaml +AM2R: + setting 1: + option 1: 50 + option 2: 0 + option 3: 0 + option 4: 0 + + setting 2: + option 1: 50 + option 2: 25 + option 3: 10 + option 4: 0 +``` +To make a setting guaranteed it needs to be the only one with any value other than 0 but if you wanted it to be random +you can add values to multiple options, and it will pick one at random with the formula of `option_value/total_setting_values`. +So for `setting 1` it would always pick `option 1` and for `setting 2` it would pick `option 1` `50/85` of the time, `option 2` `25/85` of the time, and `option 3` `10/85` of the time. + +After setting up the `.yaml` file you can save it and then send it to the host. + +### Joining the Session + +After the host has generated the seed and has given you the connection information you can use the `Archipelago Launcher` +to run the `AM2R Client` after that run the AM2R Multiworld Mod and go to the settings to find the `Multiworld` menu to enter +the slot name (player name) and hit `Connect to Python Client` the client will then show `Successfully Connected to AM2R` +at which point you can run /connect [IP]:[PORT] or use the address bar at the top with the same formatting to connect to the +multiworld session at which point you can start a new save file or continue a save file. + +# Things are subject to change at developer discretion + +### Seeds should be beatable but its possible there are still logical errors + +## Bug Reporting/Feature Requests + +If you are playing this mod and are encountering bugs **DO NOT** report them to the Archipelago server this is not official support from them, and they may or may not know what to do instead you can report them to the dedicated mod thread on the [AM2R Server](https://discord.gg/YTQnkAJ) or on the [Modding Server](https://discord.gg/Fdq3MSXEDb) and you can always open an issue on the github. + +### Help I don't have enough energy to do this check with 100% trap fill on fusion mode + +Fuck you, if the testers can do it you can too, and if you cant do it play on an easier difficulty. + +> "no one plays on hard dude" - a tester + +## Randomizer Information + +### What is randomized? + +All items both majors and minors are randomized together in one pool with the option to add in the metroids as checks as well. + +### What is the goal? + +The goal of this AM2R randomizer implementation is to kill or collect the self specified number of metroids to reach the queen metroid and save the baby metroid. + +This message sponsored by "The Galactic Federation" + +### I softlocked myself + +Don't worry all items are saved by the server, but you should in theory never need to softlock yourself to beat the game unless I messed up diff --git a/worlds/am2r/items.py b/worlds/am2r/items.py new file mode 100644 index 000000000000..361c89e8a7d0 --- /dev/null +++ b/worlds/am2r/items.py @@ -0,0 +1,63 @@ +from itertools import groupby +from typing import Dict, List, Set, NamedTuple +from BaseClasses import ItemClassification + + +class AM2RItemData(NamedTuple): + classification: ItemClassification + game_id: int + quantity_in_item_pool: int + item_group: str = "" + + +item_base_id = 8678000 + +item_table: Dict[str, AM2RItemData] = { + "Missile": AM2RItemData(ItemClassification.filler, 15, 0), + "Missile Launcher": AM2RItemData(ItemClassification.progression, 300, 0), + + "Super Missile": AM2RItemData(ItemClassification.filler, 16, 0), + "Super Missile Launcher": AM2RItemData(ItemClassification.progression, 300, 0), + + "Power Bomb": AM2RItemData(ItemClassification.filler, 18, 0), + "Power Bomb Launcher": AM2RItemData(ItemClassification.progression, 300, 0), + + "Energy Tank": AM2RItemData(ItemClassification.filler, 17, 0), + + "Morph Ball": AM2RItemData(ItemClassification.progression, 300, 0), + "Power Grip": AM2RItemData(ItemClassification.progression, 300, 0), + "Bombs": AM2RItemData(ItemClassification.progression, 0, 1), + "Spider Ball": AM2RItemData(ItemClassification.progression, 2, 1), + "Hi Jump": AM2RItemData(ItemClassification.progression, 4, 1), + "Spring Ball": AM2RItemData(ItemClassification.progression, 3, 1), + + "Space Jump": AM2RItemData(ItemClassification.progression, 6, 1), + "Speed Booster": AM2RItemData(ItemClassification.progression, 7, 1), + "Screw Attack": AM2RItemData(ItemClassification.progression, 8, 1), + + "Varia Suit": AM2RItemData(ItemClassification.progression, 5, 1), + "Gravity Suit": AM2RItemData(ItemClassification.progression, 9, 1), + + "Arm Cannon": AM2RItemData(ItemClassification.progression, 300, 0), + "Charge Beam": AM2RItemData(ItemClassification.progression, 10, 1), + "Wave Beam": AM2RItemData(ItemClassification.useful, 12, 1), + "Spazer": AM2RItemData(ItemClassification.useful, 13, 1), + "Plasma Beam": AM2RItemData(ItemClassification.useful, 14, 1), + "Ice Beam": AM2RItemData(ItemClassification.progression, 11, 1), + + "Flood Trap": AM2RItemData(ItemClassification.trap, 21, 0), + "Big Toss Trap": AM2RItemData(ItemClassification.trap, 22, 0), + "Short Beam": AM2RItemData(ItemClassification.trap, 23, 0), + "EMP Trap": AM2RItemData(ItemClassification.trap, 24, 0), + "OHKO Trap": AM2RItemData(ItemClassification.trap, 25, 0), + "Touhou Trap": AM2RItemData(ItemClassification.trap, 26, 0), + + "Metroid": AM2RItemData(ItemClassification.progression_skip_balancing, 19, 0) + +} + +item_name_to_id: Dict[str, int] = {name: item_base_id + data.game_id for name, data in item_table.items()} + +filler_items: List[str] = [name for name, data in item_table.items() if data.classification == ItemClassification.filler] + +trap_items: List[str] = [name for name, data in item_table.items() if data.classification == ItemClassification.trap] diff --git a/worlds/am2r/locations.py b/worlds/am2r/locations.py new file mode 100644 index 000000000000..a445e1d43471 --- /dev/null +++ b/worlds/am2r/locations.py @@ -0,0 +1,202 @@ +from typing import Dict, Set, NamedTuple +from itertools import groupby + + +class AM2RLocationData(NamedTuple): + region: str + game_id: int = 0 + location_group: str = "region" + + +location_based_id = 8678000 + +location_table: Dict[str, AM2RLocationData] = { + "Main Caves: Spider Ball Challenge Upper": AM2RLocationData("Main Caves", 53), + "Main Caves: Spider Ball Challenge Lower": AM2RLocationData("Main Caves", 52), + "Main Caves: Hi-Jump Challenge": AM2RLocationData("Main Caves", 57), + "Main Caves: Spiky Maze": AM2RLocationData("Main Caves", 210), + "Main Caves: Shinespark Before The Pit": AM2RLocationData("Main Caves", 54), + "Main Caves: Shinespark After The Pit": AM2RLocationData("Main Caves", 55), + + "Golden Temple: Bombs": AM2RLocationData("Golden Temple", 0), + "Golden Temple: Below Bombs": AM2RLocationData("Golden Temple", 100), + "Golden Temple: Hidden Energy Tank": AM2RLocationData("Golden Temple", 103), + "Golden Temple: Charge Beam": AM2RLocationData("Golden Temple", 10), + "Golden Temple: Armory Left": AM2RLocationData("Golden Temple", 104), + "Golden Temple: Armory Upper": AM2RLocationData("Golden Temple", 106), + "Golden Temple: Armory Lower": AM2RLocationData("Golden Temple", 105), + "Golden Temple: Armory False Wall": AM2RLocationData("Golden Temple", 107), + "Golden Temple: 3-Orb Hallway Left": AM2RLocationData("Golden Temple", 101), + "Golden Temple: 3-Orb Hallway Middle": AM2RLocationData("Golden Temple", 108), + "Golden Temple: 3-Orb Hallway Right": AM2RLocationData("Golden Temple", 102), + "Golden Temple: Spider Ball": AM2RLocationData("Golden Temple", 2), + "Golden Temple: Exterior Ceiling": AM2RLocationData("Golden Temple", 109), + "Golden Temple: EMP Room": AM2RLocationData("Golden Temple", 110), + + "Guardian: Up Above": AM2RLocationData("Guardian", 111), + "Guardian: Behind The Door": AM2RLocationData("Guardian", 112,), + + "Hydro Station: Cliff": AM2RLocationData("Hydro Station", 163), + "Hydro Station: Side Morph Tunnel": AM2RLocationData("Hydro Station", 152), + "Hydro Station: Turbine Room": AM2RLocationData("Hydro Station", 150), + "Hydro Station: Not so Secret Tunnel": AM2RLocationData("Hydro Station", 151), + "Hydro Station: Water Pressure Pre-Varia": AM2RLocationData("Hydro Station", 159), + "Hydro Station: Varia Suit": AM2RLocationData("Hydro Station", 5), + "Hydro Station: EMP Room": AM2RLocationData("Hydro Station", 162), + + "Arachnus: Boss": AM2RLocationData("Arachnus", 3), + + "Hydro Station: Wave Beam": AM2RLocationData("Inner Hydro Station", 12), + "Hydro Station: Below Tower Pipe Upper": AM2RLocationData("Inner Hydro Station", 153), + "Hydro Station: Below Tower Pipe Lower": AM2RLocationData("Inner Hydro Station", 154), + "Hydro Station: Dead End": AM2RLocationData("Inner Hydro Station", 155), + "Hydro Station: Hi-Jump Boots": AM2RLocationData("Inner Hydro Station", 4), + "Hydro Station: Behind Hi-Jump Boots Upper": AM2RLocationData("Inner Hydro Station", 156), + "Hydro Station: Behind Hi-Jump Boots Lower": AM2RLocationData("Inner Hydro Station", 157), + + "Hydro Nest: Below the Walkway": AM2RLocationData("Hydro Nest", 158), + "Hydro Nest: Speed Ceiling": AM2RLocationData("Hydro Nest", 161), + "Hydro Nest: Behind The Wall": AM2RLocationData("Hydro Nest", 160), + + "Industrial Complex: Above Save": AM2RLocationData("Industrial Complex Nest", 214), + "Industrial Complex: EMP Room": AM2RLocationData("Industrial Complex Nest", 213), + "Industrial Complex Nest: Nest Shinespark": AM2RLocationData("Industrial Complex Nest", 209), + + "Industrial Complex: In the Sand": AM2RLocationData("Pre Industrial Complex", 211), + "Industrial Complex: Complex Side After Tunnel": AM2RLocationData("Pre Industrial Complex", 202), + "Industrial Complex: Complex Side Tunnel": AM2RLocationData("Pre Industrial Complex", 200), + "Industrial Complex: Behind the Green Door": AM2RLocationData("Pre Industrial Complex", 212), + "Industrial Complex: Save Room": AM2RLocationData("Pre Industrial Complex", 203), + "Industrial Complex: Spazer Beam": AM2RLocationData("Pre Industrial Complex", 13), + "Industrial Complex: Sisyphus Spark": AM2RLocationData("Pre Industrial Complex", 204), + "Industrial Complex: Speed Booster": AM2RLocationData("Pre Industrial Complex", 7), + + "Torizo Ascended: Boss": AM2RLocationData("Torizo Ascended", 6), + + "Industrial Complex: Conveyor Belt Room": AM2RLocationData("Industrial Complex", 205), + "Industrial Complex: Doom Treadmill": AM2RLocationData("Industrial Complex", 201), + "Industrial Complex: Complex Hub Shinespark" AM2RLocationData("Industrial Complex", 208), + "Industrial Complex: Complex Hub in the Floor": AM2RLocationData("Industrial Complex", 207), + "Industrial Complex: Skippy Reward": AM2RLocationData("Industrial Complex", 206), + + "GFS Thoth: Research Camp": AM2RLocationData("GFS Thoth", 215), + "GFS Thoth: Hornoad Room": AM2RLocationData("GFS Thoth", 58), + "GFS Thoth: Outside the Front of the Ship": AM2RLocationData("GFS Thoth", 59), + + "Genesis: Boss": AM2RLocationData("Genesis", 50), + + "The Tower: Beside Hydro Pipe": AM2RLocationData("The Tower", 259), + "The Tower: Right Side of Tower": AM2RLocationData("The Tower", 250), + "The Tower: In the Ceiling": AM2RLocationData("The Tower", 257), + "The Tower: Dark Maze": AM2RLocationData("The Tower", 252), + "The Tower: After Dark Maze": AM2RLocationData("The Tower", 251), + "The Tower: Plasma Beam": AM2RLocationData("The Tower", 14), + "The Tower: After Tester": AM2RLocationData("The Tower", 256), + "The Tower: Outside Reactor": AM2RLocationData("The Tower", 258), + + "The Tower: Geothermal Reactor": AM2RLocationData("Geothermal", 253), + "The Tower: Post Reactor Chozo": AM2RLocationData("Geothermal", 254), + "The Tower: Post Reactor Shinespark": AM2RLocationData("Geothermal", 255), + + "Distribution Center: Main Room Shinespark": AM2RLocationData("Underwater Distribution Center", 309), + "Distribution Center: Underwater Speed Hallway": AM2RLocationData("Underwater Distribution Center", 307), + + "Distribution Center: After EMP Activation": AM2RLocationData("EMP", 300), + + "Distribution Center: Spider Ball Crumble Spiky \"Maze\"": AM2RLocationData("Underwater Distro Connection", 303), + "Distribution Center: Before Spiky Trial": AM2RLocationData("Underwater Distro Connection", 304), + "Distribution Center: Spiky Trial Shinespark": AM2RLocationData("Underwater Distro Connection", 305), + "Distribution Center: After Spiky Trial": AM2RLocationData("Underwater Distro Connection", 306), + + "Distribution Center: Screw Attack": AM2RLocationData("Screw Attack", 8), + "Distribution Center: Exterior Post-Gravity": AM2RLocationData("Pipe Hell Outside", 302), + "Distribution Center: Spectator Jail": AM2RLocationData("Pipe Hell R", 301), + + "Distribution Center: Before Gravity": AM2RLocationData("Gravity", 308), + "Distribution Center: Gravity Suit": AM2RLocationData("Gravity", 9), + + "Serris: Ice Beam": AM2RLocationData("Ice Beam", 11), + + "Deep Caves: Drivel Ballspark": AM2RLocationData("Deep Caves", 56), + "Deep Caves: Ramulken Lava Pool": AM2RLocationData("Deep Caves", 60), + + "Deep Caves: After Omega": AM2RLocationData("Deep Caves", 51), + + "The Forgotten Alpha": AM2RLocationData("First Alpha", 310), + + "Golden Temple: Friendly Spider": AM2RLocationData("Golden Temple", 311), + + "Golden Temple Nest: Moe": AM2RLocationData("Golden Temple Nest", 312), + "Golden Temple Nest: Larry": AM2RLocationData("Golden Temple Nest", 313), + "Golden Temple Nest: Curly": AM2RLocationData("Golden Temple Nest", 314), + + "Main Caves: Freddy Fazbear": AM2RLocationData("Main Caves", 315), + + "Hydro Station: Turbine Terror": AM2RLocationData("Hydro Station", 316), + "Hydro Station: The Lookout": AM2RLocationData("Hydro Station", 318), + "Hydro Station: Recent Guardian": AM2RLocationData("Hydro Station", 317), + + "Hydro Nest: EnderMahan": AM2RLocationData("Hydro Nest", 319), + "Hydro Nest: Carnage Awful": AM2RLocationData("Hydro Nest", 320), + "Hydro Nest: Venom Awesome": AM2RLocationData("Hydro Nest", 321), + "Hydro Nest: Something More, Something Awesome": AM2RLocationData("Hydro Nest", 322), + + "Industrial Nest: Mimolette": AM2RLocationData("Industrial Complex Nest", 326), + "Industrial Nest: The Big Cheese": AM2RLocationData("Industrial Complex Nest", 327), + "Industrial Nest: Mohwir": AM2RLocationData("Industrial Complex Nest", 328), + "Industrial Nest: Chirn": AM2RLocationData("Industrial Complex Nest", 329), + "Industrial Nest: BHHarbinger": AM2RLocationData("Industrial Complex Nest", 330), + "Industrial Nest: The Abyssal Creature": AM2RLocationData("Industrial Complex Nest", 331), + + "Industrial Complex: Sisyphus": AM2RLocationData("Pre Industrial Complex", 323), + "Industrial Complex: And then there\'s this Asshole": AM2RLocationData("Pre Industrial Complex", 332), + + "Inside Industrial: Guardian of Doom Treadmill": AM2RLocationData("Industrial Complex", 324), + "Inside Industrial: Rawsome1234 by the Lava Lake": AM2RLocationData("Industrial Complex", 325), + + "Dual Alphas: Marco": AM2RLocationData("GFS Thoth", 333), + "Dual Alphas: Polo": AM2RLocationData("GFS Thoth", 334), + + "Mines: Unga": AM2RLocationData("Mines", 335), + "Mines: Gunga": AM2RLocationData("Mines", 336), + + "The Tower: Patricia": AM2RLocationData("The Tower", 337), + "The Tower: Variable \"GUH\"": AM2RLocationData("The Tower", 338), + "Ruler of The Tower: Slagathor": AM2RLocationData("The Tower", 340), + "The Tower: Mr.Sandman": AM2RLocationData("The Tower", 339), + "The Tower: Anakin": AM2RLocationData("The Tower", 341), + "The Tower: Xander": AM2RLocationData("The Tower", 342), + + "EMP: Sir Zeta Commander of the Alpha Squadron": AM2RLocationData("EMP", 343), + + "Alpha Squadron: Timmy": AM2RLocationData("Pipe Hell R", 346), + "Alpha Squadron: Tommy": AM2RLocationData("Pipe Hell R", 345), + "Alpha Squadron: Terry": AM2RLocationData("Pipe Hell R", 348), + "Alpha Squadron: Telly": AM2RLocationData("Pipe Hell R", 347), + "Alpha Squadron: Martin": AM2RLocationData("Pipe Hell R", 344), + + "Underwater: Gamma Bros Mario": AM2RLocationData("Underwater Distro Connection", 349), + "Underwater: Gamma Bros Luigi": AM2RLocationData("Underwater Distro Connection", 350), + + "Deep Caves: Lil\' Bro": AM2RLocationData("Deep Caves", 351), + "Deep Caves: Big Sis": AM2RLocationData("Deep Caves", 352), + "Omega Nest: Lucina": AM2RLocationData("Omega Nest", 355), + "Omega Nest: Epsilon": AM2RLocationData("Omega Nest", 354), + "Omega Nest: Druid": AM2RLocationData("Omega Nest", 353), + + "The Last Metroid is in Captivity": AM2RLocationData("Research Station", None), +} + +location_name_to_id: Dict[str, int] = {name: location_based_id + index for index, name in enumerate(location_table)} + + +def get_location_group(location_name: str) -> str: + loc_group = location_table[location_name].location_group + if loc_group == "region": + loc_group = location_table[location_name].region.lower() + return loc_group + + +location_name_group: Dict[str, Set[str]] = { + group: set(location_names) for group, location_names in groupby(sorted(location_table, key=get_location_group), get_location_group) if group != "" +} diff --git a/worlds/am2r/options.py b/worlds/am2r/options.py new file mode 100644 index 000000000000..77f14fb74451 --- /dev/null +++ b/worlds/am2r/options.py @@ -0,0 +1,160 @@ +from dataclasses import dataclass + +from Options import DefaultOnToggle, Toggle, StartInventoryPool, Choice, Range, PerGameCommonOptions + + +class LogicDificulty(Choice): + """Easy: Assumes developer intended solutions and expects little to none trick knowledge and less than optimal use of ammos + Required Tricks: Simple single wall Wall-Jumps, Knowledge of the Low% Super Missile block in Distribution Center + Required Damage: Hydro Station Nest Vines with 2 E-Tanks + Normal: Assumes knowledge over the game and expects some trick knowledge + Required Tricks: Simple Morph Glides, tricky single wall Wall-Jumps, Knowledge of Shinesparks to reach areas + Required Damage: Hydro Nest Vines (1E), Doom Treadmill Spikes, Spikes to Patricia(A4 Right Side Zeta)""" + display_name = "Logic Dificulty" + default = 0 + option_easy = 0 + option_normal = 1 + option_hard = 2 # todo add hard mode definitions + + +class AmmoLogic(Choice): + """Normal/Easy: Assumes you get 5 missiles and 2 Super Missiles/Power Bombs per pack + Fusion/Hard: Assumes you get 2 missiles and 1 Super Missile/Power Bomb per pack + picking normal/easy and then playing on fusion or hard difficulty could make the game impossible""" + display_name = "Ammo Logic" + default = 0 + option_normal = 0 + option_fusion = 1 + ailias_hard = 1 + alias_easy = 0 + + +class MetroidsRequired(Range): + """Chose how many Metroids need to be killed or obtained to go through to the omega nest""" + display_name = "Metroids Required for Omega Nest" + range_start = 0 + range_end = 46 + default = 46 + + +class MetroidsAreChecks(Choice): # I kinda want to explode this option and have it always be on + """Have each of the 46 non lab Metroids be treated as locations""" + display_name = "Metroids are Checks" + default = 2 + option_disabled = 0 + option_exclude_A6 = 1 + option_include_A6 = 2 + + +class StartLocation(Toggle): + """Randomize Starting Location""" + display_name = "Randomize Start Location" + + +class AreaRando(Choice): + """Activates Area Randomization and or Boss Randomization, also activates rolling saves as softlock prevention + Area Randomizer will shuffle various Areas arround in order to create a new expierence + Boss Randomization randomizes Arachnus, Torizo Ascended, and Genesis with each other also then randomizes + Temple Guardian, Tester and Serris + Both activates Both independently on their own""" + display_name = "Area Randomizer" + default = 0 + option_disabled = 0 + option_boss_rando = 1 + option_area_rando = 2 + + +class RemovePowerGrip(Toggle): + """Adds Power Grip to the item pool and removes it from the start inventory""" + display_name = "Remove Power Grip" + + +class RemoveMorphBall(Toggle): + """Adds Morph Ball to the item pool and removes it from the start inventory""" + display_name = "Remove Morph Ball" + + +class RemoveBeam(Toggle): + """Removes your Arm Cannon and makes it a findable item""" + display_name = "Remove Beam" + + +class MissileLauncher(Choice): + """Removes the 30 Starting Missiles or add a missile launcher to the item pool""" + display_name = "Remove Missiles" + default = 0 + option_vanilla = 0 + option_remove = 1 + option_launcher = 2 + + +class SuperLauncher(Toggle): + """Super Missile Launcher""" + display_name = "Super Missile Launcher" + + +class PowerLauncher(Toggle): + """Power Bomb Launcher""" + display_name = "Power Bomb Launcher" + + +class TrapFillPercentage(Range): + """Adds in slightly inconvenient traps into the item pool""" + display_name = "Trap Fill Percentage" + range_start = 0 + range_end = 100 + default = 0 + + +class RemoveFloodTrap(Toggle): + """Removes Flood Traps from trap fill""" + display_name = "Remove Flood Trap" + + +class RemoveTossTrap(Toggle): + """There is a pipebomb in your mailbox""" + display_name = "Remove Toss Trap" + + +class RemoveShortBeam(Toggle): + """Remove muscle memory trap""" + display_name = "Remove Short Beam" + + +class RemoveEMPTrap(Toggle): + """Yes we know that it looks weird during the idle animation, but it's a vanilla bug""" + display_name = "Remove EMP Trap" + + +class RemoveTouhouTrap(Toggle): + """Removes Touhou Traps from trap fill""" + display_name = "Remove Touhou Trap" + + +class RemoveOHKOTrap(Toggle): + """Removes OHKO Traps from trap fill""" + display_name = "Remove OHKO Trap" + + +@dataclass +class AM2ROptions(PerGameCommonOptions): + logic_dificulty: LogicDificulty + ammo_logic: AmmoLogic + metroids_required: MetroidsRequired + metroids_are_checks: MetroidsAreChecks + start_location: StartLocation + area_rando: AreaRando + remove_power_grip: RemovePowerGrip + remove_morph_ball: RemoveMorphBall + remove_beam: RemoveBeam + missile_launcher: MissileLauncher + super_launcher: SuperLauncher + power_launcher: PowerLauncher + trap_fill_percentage: TrapFillPercentage + remove_flood_trap: RemoveFloodTrap + remove_toss_trap: RemoveTossTrap + remove_short_beam: RemoveShortBeam + remove_EMP_trap: RemoveEMPTrap + remove_touhou_trap: RemoveTouhouTrap + remove_OHKO_trap: RemoveOHKOTrap + start_inventory_pool: StartInventoryPool diff --git a/worlds/am2r/regions.py b/worlds/am2r/regions.py new file mode 100644 index 000000000000..6f1984592314 --- /dev/null +++ b/worlds/am2r/regions.py @@ -0,0 +1,267 @@ +from typing import List, Set, Dict, Tuple, Optional, Callable, NamedTuple +from BaseClasses import CollectionState, MultiWorld, Region, Entrance, Location +from .locations import AM2RLocationData, get_location_datas +from .rules import AM2RLogic + +EventId: Optional[int] = None + + +class LocationData(NamedTuple): + region: str + name: str + code: Optional[int] + rule: Callable[[CollectionState], bool] = lambda state: True + + +def create_regions_and_locations(world: MultiWorld, player: int): + location_datas: Tuple[AM2RLocationData] = get_location_datas(world, player) + + locations_per_region: Dict[str, List[AM2RLocationData]] = split_location_datas_per_region(location_datas) + + regions = [ + create_region(world, player, locations_per_region, "Menu"), + create_region(world, player, locations_per_region, "Main Caves"), + create_region(world, player, locations_per_region, "First Alpha"), + create_region(world, player, locations_per_region, "Guardian"), + create_region(world, player, locations_per_region, "Golden Temple"), + create_region(world, player, locations_per_region, "Golden Temple Nest"), + create_region(world, player, locations_per_region, "Hydro Station"), + create_region(world, player, locations_per_region, "Inner Hydro Station"), + create_region(world, player, locations_per_region, "Hydro Nest"), + create_region(world, player, locations_per_region, "Arachnus"), + create_region(world, player, locations_per_region, "Mines"), + create_region(world, player, locations_per_region, "Industrial Complex Nest"), + create_region(world, player, locations_per_region, "Pre Industrial Complex"), + create_region(world, player, locations_per_region, "Industrial Complex"), + create_region(world, player, locations_per_region, "Torizo Ascended"), + create_region(world, player, locations_per_region, "The Tower"), + create_region(world, player, locations_per_region, "Geothermal"), + create_region(world, player, locations_per_region, "Tester"), + create_region(world, player, locations_per_region, "Tester Upper"), + create_region(world, player, locations_per_region, "Tester Lower"), + create_region(world, player, locations_per_region, "Underwater Distribution Center"), + create_region(world, player, locations_per_region, "Underwater Distro Connection"), + create_region(world, player, locations_per_region, "Serris"), + create_region(world, player, locations_per_region, "Ice Beam"), + create_region(world, player, locations_per_region, "Pipe Hell BL"), + create_region(world, player, locations_per_region, "Pipe Hell BR"), + create_region(world, player, locations_per_region, "Pipe Hell L"), + create_region(world, player, locations_per_region, "Pipe Hell R"), + create_region(world, player, locations_per_region, "Pipe Hell Outside"), + create_region(world, player, locations_per_region, "Fast Travel"), + create_region(world, player, locations_per_region, "Gravity"), + create_region(world, player, locations_per_region, "EMP"), + create_region(world, player, locations_per_region, "Screw Attack"), + create_region(world, player, locations_per_region, "GFS Thoth"), + create_region(world, player, locations_per_region, "Genesis"), + create_region(world, player, locations_per_region, "Deep Caves"), + create_region(world, player, locations_per_region, "Omega Nest"), + create_region(world, player, locations_per_region, "The Lab"), + create_region(world, player, locations_per_region, "Research Station") + ] + + if __debug__: + throwIfAnyLocationIsNotAssignedToARegion(regions, locations_per_region.keys()) + + world.regions += regions + + logic = AM2RLogic(world, player) + + connect(world, player, "Menu", "Main Caves"), + + connect(world, player, "Main Caves", "Guardian"), + connect(world, player, "Guardian", "Main Caves"), + + connect(world, player, "Main Caves", "First Alpha"), + connect(world, player, "First Alpha", "Main Caves"), + + connect(world, player, "Main Caves", "Hydro Station"), + connect(world, player, "Hydro Station", "Main Caves"), + + connect(world, player, "Main Caves", "Mines", lambda state: state.has("Super Missile", player)), + connect(world, player, "Mines", "Main Caves"), + + connect(world, player, "Main Caves", "Industrial Complex Nest"), + connect(world, player, "Industrial Complex Nest", "Main Caves"), + + connect(world, player, "Main Caves", "The Tower"), + connect(world, player, "The Tower", "Main Caves"), + + connect(world, player, "Main Caves", "Underwater Distribution Center", lambda state: (state.has("Power Bomb", player) or state.has("Super Missile", player)) and state.has("Ice Beam", player)), # when s&q is fixed, remove ice beam + connect(world, player, "Underwater Distribution Center", "Main Caves", lambda state: state.has("Ice Beam", player)), + + connect(world, player, "Main Caves", "Deep Caves", logic.AM2R_can_down), + connect(world, player, "Deep Caves", "Main Caves"), + + connect(world, player, "Main Caves", "GFS Thoth"), + connect(world, player, "GFS Thoth", "Main Caves"), + + connect(world, player, "GFS Thoth", "Genesis") + connect(world, player, "GFS Thoth", "Genesis") + + connect(world, player, "Guardian", "Golden Temple"), + connect(world, player, "Golden Temple", "Guardian"), + + connect(world, player, "Guardian", "Golden Temple Nest"), + connect(world, player, "Golden Temple Nest", "Guardian"), + + connect(world, player, "Golden Temple", "Fast Travel", lambda state: state.has("Screw Attack", player)), + connect(world, player, "Fast Travel", "Golden Temple", lambda state: state.has("Screw Attack", player)), + + connect(world, player, "Hydro Station", "Hydro Nest", logic.AM2R_can_jump), + connect(world, player, "Hydro Nest", "Hydro Station"), + + connect(world, player, "Hydro Station", "The Tower", lambda state: state.has("Screw Attack", player)), + connect(world, player, "The Tower", "Hydro Station", lambda state: state.has("Screw Attack", player)), + + connect(world, player, "Hydro Station", "The Lab", logic.AM2R_can_lab), + connect(world, player, "The Lab", "Hydro Station", lambda state: state.has("Screw Attack", player)), + + connect(world, player, "Hydro Station", "Arachnus", lambda state: state.has("Power Bomb", player, 4) or state.has("Bombs", player)), + connect(world, player, "Arachnus", "Hydro Station"), + + connect(world, player, "Hydro Station", "Inner Hydro Station", lambda state: state.has("Screw Attack", player) or logic.AM2R_can_bomb(state)) + connect(world, player, "Inner Hydro Station", "Hydro Station", lambda state: state.has("Screw Attack", player) or logic.AM2R_can_bomb(state)) + + connect(world, player, "Industrial Complex Nest", "Pre Industrial Complex", lambda state: state.has("Speed Booster", player) or logic.AM2R_can_bomb(state)), + connect(world, player, "Pre Industrial Complex", "Industrial Complex Nest", lambda state: state.has("Speed Booster", player) or logic.AM2R_can_bomb(state)), + + connect(world, player, "Pre Industrial Complex", "Industrial Complex"), + connect(world, player, "Industrial Complex", "Pre Industrial Complex", lambda state: state.has("Speed Booster", player)), + + connect(world, player, "Pre Industrial Complex", "Fast Travel", lambda state: state.has("Screw Attack", player)), + connect(world, player, "Fast Travel", "Pre Industrial Complex", lambda state: state.has("Screw Attack", player)), + + connect(world, player, "Pre Industrial Complex", "Torizo Ascended"), + connect(world, player, "Torizo Ascended", "Pre Industrial Complex"), + # A4 to Geothermal + connect(world, player, "The Tower", "Geothermal", lambda state: state.has("Speed Booster", player) and state.has("Power Bomb", player)), + connect(world, player, "Geothermal", "The Tower", lambda state: state.has("Speed Booster", player) and state.has("Power Bomb", player)), + # tower to A5 + connect(world, player, "The Tower", "Fast Travel", lambda state: state.has("Screw Attack", player)), + connect(world, player, "Fast Travel", "The Tower", lambda state: state.has("Screw Attack", player)), + # tester + connect(world, player, "The Tower", "Tester Lower", logic.AM2R_can_bomb), + connect(world, player, "The Tower", "Tester Upper", logic.AM2R_can_bomb), + connect(world, player, "Tester Lower", "Tester"), + connect(world, player, "Tester", "Tester Lower"), + connect(world, player, "Tester", "Tester Upper"), + connect(world, player, "Tester Upper", "Tester"), + connect(world, player, "Tester Lower", "The Tower", logic.AM2R_can_bomb), + connect(world, player, "Tester Upper", "The Tower", logic.AM2R_can_bomb), + # A5 + connect(world, player, "Underwater Distribution Center", "EMP", logic.AM2R_can_bomb), + connect(world, player, "EMP", "Underwater Distribution Center", logic.AM2R_can_bomb), + + connect(world, player, "Underwater Distribution Center", "Serris"), + connect(world, player, "Serris", "Underwater Distribution Center", lambda state: state.has("Gravity Suit", player)), + + connect(world, player, "Ice Beam", "Serris"), + connect(world, player, "Serris", "Ice Beam", lambda state: state.has("Gravity Suit", player)), + + # Pipe Hell Fuckery + connect(world, player, "EMP", "Pipe Hell BL"), + connect(world, player, "Pipe Hell BL", "EMP"), + + connect(world, player, "Pipe Hell BL", "Pipe Hell BR"), + connect(world, player, "Pipe Hell BR", "Pipe Hell BL"), + + connect(world, player, "Pipe Hell L", "Pipe Hell BL", lambda state: state.has("Screw Attack", player)), + connect(world, player, "Pipe Hell BL", "Pipe Hell L", lambda state: state.has("Screw Attack", player)), + + connect(world, player, "Pipe Hell BR", "Pipe Hell L"), + connect(world, player, "Pipe Hell L", "Pipe Hell BR"), + + connect(world, player, "Pipe Hell BR", "Pipe Hell R", lambda state: state.has("Screw Attack", player)), + connect(world, player, "Pipe Hell R", "Pipe Hell BR", lambda state: state.has("Screw Attack", player)), + + connect(world, player, "Pipe Hell R", "Pipe Hell L", logic.AM2R_can_bomb), + connect(world, player, "Pipe Hell L", "Pipe Hell R", logic.AM2R_can_bomb), + + connect(world, player, "Pipe Hell L", "Fast Travel", lambda state: state.has("Screw Attack", player)), + connect(world, player, "Fast Travel", "Pipe Hell L", lambda state: state.has("Screw Attack", player)), + + connect(world, player, "Fast Travel", "Gravity", lambda state: state.has("Gravity Suit", player)), # one way transition due to crumbles + + connect(world, player, "Fast Travel", "Underwater Distribution Center"), + connect(world, player, "Underwater Distribution Center", "Fast Travel", lambda state: state.can_reach("Fast Travel", "Region", player)), + + connect(world, player, "Gravity", "Pipe Hell Outside", lambda state: state.has("Gravity Suit", player) and state.has("Space Jump", player)), + connect(world, player, "Pipe Hell Outside", "Gravity"), + + connect(world, player, "Pipe Hell Outside", "Pipe Hell R", logic.AM2R_can_bomb), + connect(world, player, "Pipe Hell R", "Pipe Hell Outside", lambda state: state.can_reach("Pipe Hell Outside", "Region", player)), + + connect(world, player, "Screw Attack", "Pipe Hell R", lambda state: state.has("Screw Attack", player) and logic.AM2R_can_schmove(state)), + connect(world, player, "Pipe Hell R", "Screw Attack", logic.AM2R_can_spider), + + connect(world, player, "Underwater Distribution Center", "Underwater Distro Connection", lambda state: state.has("Ice Beam", player) or (state.has("Gravity Suit", player) and state.has("Speed Booster", player))), + connect(world, player, "Underwater Distro Connection", "Underwater Distribution Center", lambda state: state.has("Ice Beam", player) or (state.has("Gravity Suit", player) and state.has("Speed Booster", player))), + + connect(world, player, "Underwater Distro Connection", "Pipe Hell R"), + connect(world, player, "Pipe Hell R", "Underwater Distro Connection", lambda state: state.has("Super Missile", player) or (state.has("Gravity Suit", player) and state.has("Speed Booster", player))) + + connect(world, player, "Deep Caves", "Omega Nest") + connect(world, player, "Omega Nest", "Deep Caves") + + connect(world, player, "Omega Nest", "The Lab", logic.AM2R_can_lab) # , logic.AM2R_can_lab + connect(world, player, "The Lab", "Omega Nest") + + connect(world, player, "The Lab", "Research Station") + + +def throwIfAnyLocationIsNotAssignedToARegion(regions: List[Region], regionNames: Set[str]): + existingRegions: Set[str] = set() + + for region in regions: + existingRegions.add(region.name) + + if regionNames - existingRegions: + raise Exception( + "AM2R: the following regions are used in locations: {}, but no such region exists".format( + regionNames - existingRegions)) + + +def create_location(player: int, location_data: LocationData, region: Region) -> Location: + location = Location(player, location_data.name, location_data.code, region) + location.access_rule = location_data.rule + + if id is None: + location.event = True + location.locked = True + + return location + + +def create_region(world: MultiWorld, player: int, locations_per_region: Dict[str, List[LocationData]],name: str) -> Region: + region = Region(name, player, world) + + if name in locations_per_region: + for location_data in locations_per_region[name]: + location = create_location(player, location_data, region) + region.locations.append(location) + + return region + + +def connect(world: MultiWorld, player: int, source: str, target: str, + rule: Optional[Callable[[CollectionState], bool]] = None): + sourceRegion = world.get_region(source, player) + targetRegion = world.get_region(target, player) + + connection = Entrance(player, "", sourceRegion) + + if rule: + connection.access_rule = rule + + sourceRegion.exits.append(connection) + connection.connect(targetRegion) + + +def split_location_datas_per_region(locations: Tuple[LocationData, ...]) -> Dict[str, List[LocationData]]: + per_region: Dict[str, List[AM2RLocationData]] = {} + + for location in locations: + per_region.setdefault(location.region, []).append(location) + + return per_region diff --git a/worlds/am2r/rules.py b/worlds/am2r/rules.py new file mode 100644 index 000000000000..282eed23b590 --- /dev/null +++ b/worlds/am2r/rules.py @@ -0,0 +1,1394 @@ +from random import Random +from typing import Dict, TYPE_CHECKING + +from worlds.generic.Rules import set_rule, forbid_item +from BaseClasses import CollectionState +from .options import AM2ROptions +from math import ceil + +if TYPE_CHECKING: + from . import AM2RWorld + + +# use only to make breakables, red door and missile block checks easier +def has_missiles(state: CollectionState, player: int, options: AM2ROptions, packs: int) -> bool: + if options.ammo_logic == 1: + packs = ceil(packs * 2.5) # on hard/fusion mode, missile packs give 2 missiles instead of 5 this value is rounded up + + if options.missile_launcher == 0: + return True + elif options.missile_launcher == 1: + return state.has("Missile", player, packs) + elif options.missile_launcher == 2: + packs -= 1 # launcher counts as 1 pack + return state.has("Missile Launcher", player) and state.has("Missile", player, packs) + + +# for super blocks and Metroid logic +def has_supers(state: CollectionState, player: int, options: AM2ROptions, packs: int) -> bool: + if options.ammo_logic == 1: + packs *= 2 # on hard/fusion mode, super missile packs give 1 super missile instead of 2 + + if options.missile_launcher: + packs -= 1 # launcher counts as 1 pack + return state.has("Super Missile Launcher", player) and state.has("Super Missile", player, packs) + else: + return state.has("Super Missile", player, packs) + + +def has_ammo(state: CollectionState, player: int, options: AM2ROptions, health: int) -> bool: + # calculate by number of missiles NOT MISSILE PACKS required to kill a metroid + m_packs = state.count("Missile", player) + s_packs = state.count("Super Missile", player) + m_count = 0 + s_count = 0 + damage = 0 + health = health + 0 + + if options.missile_launcher == 0: # default starting 30 missiles + m_count += 30 + + if options.missile_launcher == 2: # missile launcher in item pool counts as 30 missiles + if state.has("Missile Launcher", player): + m_count += 30 + else: # if no launcher, then you cant fire missiles and they should not be counted towards damage total + m_packs = 0 + + if options.super_launcher: + if state.has("Super Missile Launcher", player): # Super Launcher counts as 1 pack + s_packs += 1 + else: # if no launcher, then you cant fire missiles and they should not be counted towards damage total + s_packs = 0 + + if options.ammo_logic == 0: + m_count += 5 * m_packs # 5 missiles per pack + s_count += 2 * s_packs # 2 supers per pack + else: + m_count += 2 * m_packs # 2 missiles per pack + s_count += 1 * s_packs # 1 super per pack + + damage = m_count + (s_count * 5) # supers are worth 5 missiles of damage + + if damage >= health: + return True + else: + return False + + +def has_powerbombs(state: CollectionState, player: int, options: AM2ROptions, packs: int) -> bool: + if options.ammo_logic == 1: + packs *= 2 + + if options.missile_launcher: + packs -= 1 # launcher counts as 1 pack + return state.has("Power Bomb Launcher", player) and state.has("Power Bomb", player, packs) + else: + return state.has("Power Bomb", player, packs) + + +def can_break_blocks(state: CollectionState, player: int, options: AM2ROptions) -> bool: + if options.remove_beam: + return (state.has("Screw Attack", player) or + can_bomb(state, player, options, 2) or has_missiles(state, player, options, 2)) + else: + return True + + +def has_energy(state: CollectionState, player: int, options: AM2ROptions, required: int) -> bool: + num = required + if options.logic_dificulty == 0: # literally baby mode + num *= 2 + return state.has("Energy Tank", player, num) + + +def can_morph(state: CollectionState, player: int, options: AM2ROptions) -> bool: + if options.remove_morph_ball: + return state.has("Morph Ball", player) + else: + return True + + +def has_grip(state: CollectionState, player: int, options: AM2ROptions) -> bool: + if options.remove_power_grip: + return state.has("Power Grip", player) + else: + return True + + +# a surprisingly common check because of the number of 2-3 block vertical morph tunnels +def can_morph_uppies(state: CollectionState, player: int, options: AM2ROptions) -> bool: + return can_morph(state, player, options) and \ + (has_grip(state, player, options) or state.has("Bombs", player) or state.has("Spider Ball", player) + or state.has("Spring Ball", player)) + + +def can_bomb(state: CollectionState, player: int, options: AM2ROptions, packs: int) -> bool: + return (can_morph(state, player, options) and + (state.has("Bombs", player) or has_powerbombs(state, player, options, packs))) + + +def can_ibj(state: CollectionState, player: int, options: AM2ROptions) -> bool: + if options.logic_dificulty != 0: + return state.has("Bombs", player) and can_morph(state, player, options) + else: + return False + + +def can_fly(state: CollectionState, player: int, options: AM2ROptions) -> bool: + return state.has("Space Jump", player) or can_ibj(state, player, options) + + +def can_hi(state: CollectionState, player: int, options: AM2ROptions) -> bool: + return state.has("Hi Jump", player) or can_fly(state, player, options) + + +def has_spider(state: CollectionState, player: int, options: AM2ROptions) -> bool: + return state.has("Spider Ball", player) and can_morph(state, player, options) + + +def has_spring(state: CollectionState, player: int, options: AM2ROptions) -> bool: + return state.has("Spring Ball", player) and can_morph(state, player, options) + + +def can_ballspark(state: CollectionState, player: int, options: AM2ROptions) -> bool: + return state.has("Speed Booster", player) and has_spring(state, player, options) + + +def can_schmove(state: CollectionState, player: int, options: AM2ROptions) -> bool: + return has_spider(state, player, options) or can_fly(state, player, options) or state.has("Hi Jump", player) + + +def can_spider(state: CollectionState, player: int, options: AM2ROptions) -> bool: + return has_spider(state, player, options) or can_fly(state, player, options) + + +def set_region_rules(world: AM2RWorld) -> None: + multiworld = world.multiworld + player = world.player + options = world.options + + + + +def set_location_rules_easy(world: AM2RWorld) -> None: # currently every location is in a reachable state + multiworld = world.multiworld + player = world.player + options = world.options + + set_rule(multiworld.get_location("Main Caves: Spider Ball Challenge Upper", player), + lambda state: True) + + set_rule(multiworld.get_location("Main Caves: Spider Ball Challenge Lower", player), + lambda state: True) + + set_rule(multiworld.get_location("Main Caves: Hi-Jump Challenge", player), + lambda state: True) + + set_rule(multiworld.get_location("Main Caves: Spiky Maze", player), + lambda state: True) + + set_rule(multiworld.get_location("Main Caves: Shinespark Before The Pit", player), + lambda state: True) + + set_rule(multiworld.get_location("Main Caves: Shinespark After The Pit", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: Bombs", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: Below Bombs", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: Hidden Energy Tank", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: Charge Beam", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: Armory Left", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: Armory Upper", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: Armory Lower", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: Armory False Wall", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: 3-Orb Hallway Left", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: 3-Orb Hallway Middle", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: 3-Orb Hallway Right", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: Spider Ball", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: Exterior Ceiling", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: EMP Room", player), + lambda state: True) + + set_rule(multiworld.get_location("Guardian: Up Above", player), + lambda state: True) + + set_rule(multiworld.get_location("Guardian: Behind The Door", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Cliff", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Side Morph Tunnel", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Turbine Room", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Not so Secret Tunnel", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Water Pressure Pre-Varia", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Varia Suit", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: EMP Room", player), + lambda state: True) + + set_rule(multiworld.get_location("Arachnus: Boss", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Wave Beam", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Below Tower Pipe Upper", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Below Tower Pipe Lower", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Dead End", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Hi-Jump Boots", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Behind Hi-Jump Boots Upper", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Behind Hi-Jump Boots Lower", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Nest: Below the Walkway", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Nest: Speed Ceiling", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Nest: Behind The Wall", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Above Save", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: EMP Room", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex Nest: Nest Shinespark", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: In the Sand", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Complex Side After Tunnel", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Complex Side Tunnel", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Behind the Green Door", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Save Room", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Spazer Beam", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Sisyphus Spark", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Speed Booster", player), + lambda state: True) + + set_rule(multiworld.get_location("Torizo Ascended: Boss", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Conveyor Belt Room", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Doom Treadmill", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Complex Hub Shinespark", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Complex Hub in the Floor", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Skippy Reward", player), + lambda state: True) + + set_rule(multiworld.get_location("GFS Thoth: Research Camp", player), + lambda state: True) + + set_rule(multiworld.get_location("GFS Thoth: Hornoad Room", player), + lambda state: True) + + set_rule(multiworld.get_location("GFS Thoth: Outside the Front of the Ship", player), + lambda state: True) + + set_rule(multiworld.get_location("Genesis: Boss", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Beside Hydro Pipe", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Right Side of Tower", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: In the Ceiling", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Dark Maze", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: After Dark Maze", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Plasma Beam", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: After Tester", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Outside Reactor", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Geothermal Reactor", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Post Reactor Chozo", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Post Reactor Shinespark", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: Main Room Shinespark", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: Underwater Speed Hallway", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: After EMP Activation", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: Spider Ball Crumble Spiky \"Maze\"", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: Before Spiky Trial", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: Spiky Trial Shinespark", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: After Spiky Trial", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: Screw Attack", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: Exterior Post-Gravity", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: Spectator Jail", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: Before Gravity", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: Gravity Suit", player), + lambda state: True) + + set_rule(multiworld.get_location("Serris: Ice Beam", player), + lambda state: True) + + set_rule(multiworld.get_location("Deep Caves: Drivel Ballspark", player), + lambda state: True) + + set_rule(multiworld.get_location("Deep Caves: Ramulken Lava Pool", player), + lambda state: True) + + set_rule(multiworld.get_location("Deep Caves: After Omega", player), + lambda state: True) + + set_rule(multiworld.get_location("The Forgotten Alpha", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: Friendly Spider", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple Nest: Moe", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple Nest: Larry", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple Nest: Curly", player), + lambda state: True) + + set_rule(multiworld.get_location("Main Caves: Freddy Fazbear", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Turbine Terror", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: The Lookout", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Recent Guardian", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Nest: EnderMahan", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Nest: Carnage Awful", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Nest: Venom Awesome", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Nest: Something More, Something Awesome", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Nest: Mimolette", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Nest: The Big Cheese", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Nest: Mohwir", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Nest: Chirn", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Nest: BHHarbinger", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Nest: The Abyssal Creature", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Sisyphus", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: And then there\'s this Asshole", player), + lambda state: True) + + set_rule(multiworld.get_location("Inside Industrial: Guardian of Doom Treadmill", player), + lambda state: True) + + set_rule(multiworld.get_location("Inside Industrial: Rawsome1234 by the Lava Lake", player), + lambda state: True) + + set_rule(multiworld.get_location("Dual Alphas: Marco", player), + lambda state: True) + + set_rule(multiworld.get_location("Dual Alphas: Polo", player), + lambda state: True) + + set_rule(multiworld.get_location("Mines: Unga", player), + lambda state: True) + + set_rule(multiworld.get_location("Mines: Gunga", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Patricia", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Variable \"GUH\"", player), + lambda state: True) + + set_rule(multiworld.get_location("Ruler of The Tower: Slagathor", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Mr.Sandman", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Anakin", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Xander", player), + lambda state: True) + + set_rule(multiworld.get_location("EMP: Sir Zeta Commander of the Alpha Squadron", player), + lambda state: True) + + set_rule(multiworld.get_location("Alpha Squadron: Timmy", player), + lambda state: True) + + set_rule(multiworld.get_location("Alpha Squadron: Tommy", player), + lambda state: True) + + set_rule(multiworld.get_location("Alpha Squadron: Terry", player), + lambda state: True) + + set_rule(multiworld.get_location("Alpha Squadron: Telly", player), + lambda state: True) + + set_rule(multiworld.get_location("Alpha Squadron: Martin", player), + lambda state: True) + + set_rule(multiworld.get_location("Underwater: Gamma Bros Mario", player), + lambda state: True) + + set_rule(multiworld.get_location("Underwater: Gamma Bros Luigi", player), + lambda state: True) + + set_rule(multiworld.get_location("Deep Caves: Lil\' Bro", player), + lambda state: True) + + set_rule(multiworld.get_location("Deep Caves: Big Sis", player), + lambda state: True) + + set_rule(multiworld.get_location("Omega Nest: Lucina", player), + lambda state: True) + + set_rule(multiworld.get_location("Omega Nest: Epsilon", player), + lambda state: True) + + set_rule(multiworld.get_location("Omega Nest: Druid", player), + lambda state: True) + + +def set_location_rules_normal(world: AM2RWorld) -> None: + multiworld = world.multiworld + player = world.player + options = world.options + + set_rule(multiworld.get_location("Main Caves: Spider Ball Challenge Upper", player), + lambda state: True) + + set_rule(multiworld.get_location("Main Caves: Spider Ball Challenge Lower", player), + lambda state: True) + + set_rule(multiworld.get_location("Main Caves: Hi-Jump Challenge", player), + lambda state: True) + + set_rule(multiworld.get_location("Main Caves: Spiky Maze", player), + lambda state: True) + + set_rule(multiworld.get_location("Main Caves: Shinespark Before The Pit", player), + lambda state: True) + + set_rule(multiworld.get_location("Main Caves: Shinespark After The Pit", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: Bombs", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: Below Bombs", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: Hidden Energy Tank", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: Charge Beam", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: Armory Left", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: Armory Upper", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: Armory Lower", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: Armory False Wall", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: 3-Orb Hallway Left", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: 3-Orb Hallway Middle", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: 3-Orb Hallway Right", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: Spider Ball", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: Exterior Ceiling", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: EMP Room", player), + lambda state: True) + + set_rule(multiworld.get_location("Guardian: Up Above", player), + lambda state: True) + + set_rule(multiworld.get_location("Guardian: Behind The Door", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Cliff", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Side Morph Tunnel", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Turbine Room", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Not so Secret Tunnel", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Water Pressure Pre-Varia", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Varia Suit", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: EMP Room", player), + lambda state: True) + + set_rule(multiworld.get_location("Arachnus: Boss", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Wave Beam", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Below Tower Pipe Upper", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Below Tower Pipe Lower", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Dead End", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Hi-Jump Boots", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Behind Hi-Jump Boots Upper", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Behind Hi-Jump Boots Lower", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Nest: Below the Walkway", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Nest: Speed Ceiling", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Nest: Behind The Wall", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Above Save", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: EMP Room", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex Nest: Nest Shinespark", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: In the Sand", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Complex Side After Tunnel", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Complex Side Tunnel", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Behind the Green Door", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Save Room", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Spazer Beam", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Sisyphus Spark", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Speed Booster", player), + lambda state: True) + + set_rule(multiworld.get_location("Torizo Ascended: Boss", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Conveyor Belt Room", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Doom Treadmill", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Complex Hub Shinespark", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Complex Hub in the Floor", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Skippy Reward", player), + lambda state: True) + + set_rule(multiworld.get_location("GFS Thoth: Research Camp", player), + lambda state: True) + + set_rule(multiworld.get_location("GFS Thoth: Hornoad Room", player), + lambda state: True) + + set_rule(multiworld.get_location("GFS Thoth: Outside the Front of the Ship", player), + lambda state: True) + + set_rule(multiworld.get_location("Genesis: Boss", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Beside Hydro Pipe", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Right Side of Tower", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: In the Ceiling", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Dark Maze", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: After Dark Maze", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Plasma Beam", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: After Tester", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Outside Reactor", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Geothermal Reactor", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Post Reactor Chozo", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Post Reactor Shinespark", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: Main Room Shinespark", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: Underwater Speed Hallway", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: After EMP Activation", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: Spider Ball Crumble Spiky \"Maze\"", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: Before Spiky Trial", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: Spiky Trial Shinespark", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: After Spiky Trial", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: Screw Attack", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: Exterior Post-Gravity", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: Spectator Jail", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: Before Gravity", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: Gravity Suit", player), + lambda state: True) + + set_rule(multiworld.get_location("Serris: Ice Beam", player), + lambda state: True) + + set_rule(multiworld.get_location("Deep Caves: Drivel Ballspark", player), + lambda state: True) + + set_rule(multiworld.get_location("Deep Caves: Ramulken Lava Pool", player), + lambda state: True) + + set_rule(multiworld.get_location("Deep Caves: After Omega", player), + lambda state: True) + + set_rule(multiworld.get_location("The Forgotten Alpha", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: Friendly Spider", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple Nest: Moe", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple Nest: Larry", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple Nest: Curly", player), + lambda state: True) + + set_rule(multiworld.get_location("Main Caves: Freddy Fazbear", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Turbine Terror", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: The Lookout", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Recent Guardian", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Nest: EnderMahan", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Nest: Carnage Awful", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Nest: Venom Awesome", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Nest: Something More, Something Awesome", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Nest: Mimolette", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Nest: The Big Cheese", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Nest: Mohwir", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Nest: Chirn", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Nest: BHHarbinger", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Nest: The Abyssal Creature", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Sisyphus", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: And then there\'s this Asshole", player), + lambda state: True) + + set_rule(multiworld.get_location("Inside Industrial: Guardian of Doom Treadmill", player), + lambda state: True) + + set_rule(multiworld.get_location("Inside Industrial: Rawsome1234 by the Lava Lake", player), + lambda state: True) + + set_rule(multiworld.get_location("Dual Alphas: Marco", player), + lambda state: True) + + set_rule(multiworld.get_location("Dual Alphas: Polo", player), + lambda state: True) + + set_rule(multiworld.get_location("Mines: Unga", player), + lambda state: True) + + set_rule(multiworld.get_location("Mines: Gunga", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Patricia", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Variable \"GUH\"", player), + lambda state: True) + + set_rule(multiworld.get_location("Ruler of The Tower: Slagathor", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Mr.Sandman", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Anakin", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Xander", player), + lambda state: True) + + set_rule(multiworld.get_location("EMP: Sir Zeta Commander of the Alpha Squadron", player), + lambda state: True) + + set_rule(multiworld.get_location("Alpha Squadron: Timmy", player), + lambda state: True) + + set_rule(multiworld.get_location("Alpha Squadron: Tommy", player), + lambda state: True) + + set_rule(multiworld.get_location("Alpha Squadron: Terry", player), + lambda state: True) + + set_rule(multiworld.get_location("Alpha Squadron: Telly", player), + lambda state: True) + + set_rule(multiworld.get_location("Alpha Squadron: Martin", player), + lambda state: True) + + set_rule(multiworld.get_location("Underwater: Gamma Bros Mario", player), + lambda state: True) + + set_rule(multiworld.get_location("Underwater: Gamma Bros Luigi", player), + lambda state: True) + + set_rule(multiworld.get_location("Deep Caves: Lil\' Bro", player), + lambda state: True) + + set_rule(multiworld.get_location("Deep Caves: Big Sis", player), + lambda state: True) + + set_rule(multiworld.get_location("Omega Nest: Lucina", player), + lambda state: True) + + set_rule(multiworld.get_location("Omega Nest: Epsilon", player), + lambda state: True) + + set_rule(multiworld.get_location("Omega Nest: Druid", player), + lambda state: True) + + +def set_location_rules_hard(world: AM2RWorld) -> None: + multiworld = world.multiworld + player = world.player + options = world.options + + set_rule(multiworld.get_location("Main Caves: Spider Ball Challenge Upper", player), + lambda state: True) + + set_rule(multiworld.get_location("Main Caves: Spider Ball Challenge Lower", player), + lambda state: True) + + set_rule(multiworld.get_location("Main Caves: Hi-Jump Challenge", player), + lambda state: True) + + set_rule(multiworld.get_location("Main Caves: Spiky Maze", player), + lambda state: True) + + set_rule(multiworld.get_location("Main Caves: Shinespark Before The Pit", player), + lambda state: True) + + set_rule(multiworld.get_location("Main Caves: Shinespark After The Pit", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: Bombs", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: Below Bombs", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: Hidden Energy Tank", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: Charge Beam", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: Armory Left", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: Armory Upper", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: Armory Lower", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: Armory False Wall", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: 3-Orb Hallway Left", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: 3-Orb Hallway Middle", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: 3-Orb Hallway Right", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: Spider Ball", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: Exterior Ceiling", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: EMP Room", player), + lambda state: True) + + set_rule(multiworld.get_location("Guardian: Up Above", player), + lambda state: True) + + set_rule(multiworld.get_location("Guardian: Behind The Door", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Cliff", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Side Morph Tunnel", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Turbine Room", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Not so Secret Tunnel", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Water Pressure Pre-Varia", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Varia Suit", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: EMP Room", player), + lambda state: True) + + set_rule(multiworld.get_location("Arachnus: Boss", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Wave Beam", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Below Tower Pipe Upper", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Below Tower Pipe Lower", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Dead End", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Hi-Jump Boots", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Behind Hi-Jump Boots Upper", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Behind Hi-Jump Boots Lower", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Nest: Below the Walkway", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Nest: Speed Ceiling", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Nest: Behind The Wall", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Above Save", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: EMP Room", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex Nest: Nest Shinespark", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: In the Sand", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Complex Side After Tunnel", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Complex Side Tunnel", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Behind the Green Door", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Save Room", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Spazer Beam", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Sisyphus Spark", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Speed Booster", player), + lambda state: True) + + set_rule(multiworld.get_location("Torizo Ascended: Boss", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Conveyor Belt Room", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Doom Treadmill", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Complex Hub Shinespark", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Complex Hub in the Floor", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Skippy Reward", player), + lambda state: True) + + set_rule(multiworld.get_location("GFS Thoth: Research Camp", player), + lambda state: True) + + set_rule(multiworld.get_location("GFS Thoth: Hornoad Room", player), + lambda state: True) + + set_rule(multiworld.get_location("GFS Thoth: Outside the Front of the Ship", player), + lambda state: True) + + set_rule(multiworld.get_location("Genesis: Boss", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Beside Hydro Pipe", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Right Side of Tower", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: In the Ceiling", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Dark Maze", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: After Dark Maze", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Plasma Beam", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: After Tester", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Outside Reactor", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Geothermal Reactor", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Post Reactor Chozo", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Post Reactor Shinespark", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: Main Room Shinespark", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: Underwater Speed Hallway", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: After EMP Activation", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: Spider Ball Crumble Spiky \"Maze\"", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: Before Spiky Trial", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: Spiky Trial Shinespark", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: After Spiky Trial", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: Screw Attack", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: Exterior Post-Gravity", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: Spectator Jail", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: Before Gravity", player), + lambda state: True) + + set_rule(multiworld.get_location("Distribution Center: Gravity Suit", player), + lambda state: True) + + set_rule(multiworld.get_location("Serris: Ice Beam", player), + lambda state: True) + + set_rule(multiworld.get_location("Deep Caves: Drivel Ballspark", player), + lambda state: True) + + set_rule(multiworld.get_location("Deep Caves: Ramulken Lava Pool", player), + lambda state: True) + + set_rule(multiworld.get_location("Deep Caves: After Omega", player), + lambda state: True) + + set_rule(multiworld.get_location("The Forgotten Alpha", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple: Friendly Spider", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple Nest: Moe", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple Nest: Larry", player), + lambda state: True) + + set_rule(multiworld.get_location("Golden Temple Nest: Curly", player), + lambda state: True) + + set_rule(multiworld.get_location("Main Caves: Freddy Fazbear", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Turbine Terror", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: The Lookout", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Station: Recent Guardian", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Nest: EnderMahan", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Nest: Carnage Awful", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Nest: Venom Awesome", player), + lambda state: True) + + set_rule(multiworld.get_location("Hydro Nest: Something More, Something Awesome", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Nest: Mimolette", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Nest: The Big Cheese", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Nest: Mohwir", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Nest: Chirn", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Nest: BHHarbinger", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Nest: The Abyssal Creature", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: Sisyphus", player), + lambda state: True) + + set_rule(multiworld.get_location("Industrial Complex: And then there\'s this Asshole", player), + lambda state: True) + + set_rule(multiworld.get_location("Inside Industrial: Guardian of Doom Treadmill", player), + lambda state: True) + + set_rule(multiworld.get_location("Inside Industrial: Rawsome1234 by the Lava Lake", player), + lambda state: True) + + set_rule(multiworld.get_location("Dual Alphas: Marco", player), + lambda state: True) + + set_rule(multiworld.get_location("Dual Alphas: Polo", player), + lambda state: True) + + set_rule(multiworld.get_location("Mines: Unga", player), + lambda state: True) + + set_rule(multiworld.get_location("Mines: Gunga", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Patricia", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Variable \"GUH\"", player), + lambda state: True) + + set_rule(multiworld.get_location("Ruler of The Tower: Slagathor", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Mr.Sandman", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Anakin", player), + lambda state: True) + + set_rule(multiworld.get_location("The Tower: Xander", player), + lambda state: True) + + set_rule(multiworld.get_location("EMP: Sir Zeta Commander of the Alpha Squadron", player), + lambda state: True) + + set_rule(multiworld.get_location("Alpha Squadron: Timmy", player), + lambda state: True) + + set_rule(multiworld.get_location("Alpha Squadron: Tommy", player), + lambda state: True) + + set_rule(multiworld.get_location("Alpha Squadron: Terry", player), + lambda state: True) + + set_rule(multiworld.get_location("Alpha Squadron: Telly", player), + lambda state: True) + + set_rule(multiworld.get_location("Alpha Squadron: Martin", player), + lambda state: True) + + set_rule(multiworld.get_location("Underwater: Gamma Bros Mario", player), + lambda state: True) + + set_rule(multiworld.get_location("Underwater: Gamma Bros Luigi", player), + lambda state: True) + + set_rule(multiworld.get_location("Deep Caves: Lil\' Bro", player), + lambda state: True) + + set_rule(multiworld.get_location("Deep Caves: Big Sis", player), + lambda state: True) + + set_rule(multiworld.get_location("Omega Nest: Lucina", player), + lambda state: True) + + set_rule(multiworld.get_location("Omega Nest: Epsilon", player), + lambda state: True) + + set_rule(multiworld.get_location("Omega Nest: Druid", player), + lambda state: True) diff --git a/worlds/am2r/todo.py b/worlds/am2r/todo.py new file mode 100644 index 000000000000..9c12a69bcb37 --- /dev/null +++ b/worlds/am2r/todo.py @@ -0,0 +1,2 @@ +# todo update the get option value to use the new options definition so the console stops yelling at me +# todo eventually add the ability to area rando