From 64e2d55e92415d29b6108de1f4af292265f0c620 Mon Sep 17 00:00:00 2001 From: PoryGone <98504756+PoryGone@users.noreply.github.com> Date: Fri, 2 Dec 2022 00:25:02 -0500 Subject: [PATCH 01/35] SMW: Bug+logic fixes (#1279) * Allow levels to scroll vertically without climb or run * Account for needing Yoshi for VB2 Dragon Coins * Don't allow messages on intro level --- worlds/smw/Client.py | 2 +- worlds/smw/Regions.py | 3 ++- worlds/smw/Rom.py | 18 ++++++++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/worlds/smw/Client.py b/worlds/smw/Client.py index 9cf5a5fcfbdb..c2981eff8e59 100644 --- a/worlds/smw/Client.py +++ b/worlds/smw/Client.py @@ -50,7 +50,7 @@ SMW_GOAL_LEVELS = [0x28, 0x31, 0x32] SMW_INVALID_MARIO_STATES = [0x05, 0x06, 0x0A, 0x0C, 0x0D] -SMW_BAD_TEXT_BOX_LEVELS = [0x26, 0x02, 0x4B] +SMW_BAD_TEXT_BOX_LEVELS = [0x00, 0x26, 0x02, 0x4B] SMW_BOSS_STATES = [0x80, 0xC0, 0xC1] SMW_UNCOLLECTABLE_LEVELS = [0x25, 0x07, 0x0B, 0x40, 0x0E, 0x1F, 0x20, 0x1B, 0x1A, 0x35, 0x34, 0x31, 0x32] diff --git a/worlds/smw/Regions.py b/worlds/smw/Regions.py index fa18d980e355..d37ad00e3833 100644 --- a/worlds/smw/Regions.py +++ b/worlds/smw/Regions.py @@ -834,7 +834,8 @@ def create_regions(world, player: int, active_locations): state.has(ItemName.super_star_active, player) and state.has(ItemName.progressive_powerup, player, 3))) add_location_to_region(world, player, active_locations, LocationName.valley_of_bowser_1_region, LocationName.valley_of_bowser_1_dragon) - add_location_to_region(world, player, active_locations, LocationName.valley_of_bowser_2_region, LocationName.valley_of_bowser_2_dragon) + add_location_to_region(world, player, active_locations, LocationName.valley_of_bowser_2_region, LocationName.valley_of_bowser_2_dragon, + lambda state: state.has(ItemName.yoshi_activate, player)) add_location_to_region(world, player, active_locations, LocationName.valley_of_bowser_3_region, LocationName.valley_of_bowser_3_dragon) add_location_to_region(world, player, active_locations, LocationName.valley_ghost_house_region, LocationName.valley_ghost_house_dragon, lambda state: state.has(ItemName.p_switch, player)) diff --git a/worlds/smw/Rom.py b/worlds/smw/Rom.py index b55e666c2294..d827c1242739 100644 --- a/worlds/smw/Rom.py +++ b/worlds/smw/Rom.py @@ -677,6 +677,22 @@ def handle_collected_paths(rom): rom.write_bytes(COLLECTED_PATHS_SUB_ADDR + 0x13, bytearray([0x6B])) # RTL +def handle_vertical_scroll(rom): + rom.write_bytes(0x285BA, bytearray([0x22, 0x90, 0xBC, 0x03])) # JSL $03BC90 + + VERTICAL_SCROLL_SUB_ADDR = 0x01BC90 + rom.write_bytes(VERTICAL_SCROLL_SUB_ADDR + 0x00, bytearray([0x4A])) # LSR + rom.write_bytes(VERTICAL_SCROLL_SUB_ADDR + 0x01, bytearray([0x4A])) # LSR + rom.write_bytes(VERTICAL_SCROLL_SUB_ADDR + 0x02, bytearray([0x4A])) # LSR + rom.write_bytes(VERTICAL_SCROLL_SUB_ADDR + 0x03, bytearray([0x4A])) # LSR + rom.write_bytes(VERTICAL_SCROLL_SUB_ADDR + 0x04, bytearray([0x08])) # PHP + rom.write_bytes(VERTICAL_SCROLL_SUB_ADDR + 0x05, bytearray([0xC9, 0x02])) # CMP #02 + rom.write_bytes(VERTICAL_SCROLL_SUB_ADDR + 0x07, bytearray([0xD0, 0x02])) # BNE +0x02 + rom.write_bytes(VERTICAL_SCROLL_SUB_ADDR + 0x09, bytearray([0xA9, 0x01])) # LDA #01 + rom.write_bytes(VERTICAL_SCROLL_SUB_ADDR + 0x0B, bytearray([0x28])) # PLP + rom.write_bytes(VERTICAL_SCROLL_SUB_ADDR + 0x0C, bytearray([0x6B])) # RTL + + def handle_music_shuffle(rom, world, player): from .Aesthetics import generate_shuffled_level_music, generate_shuffled_ow_music, level_music_address_data, ow_music_address_data @@ -808,6 +824,8 @@ def patch_rom(world, rom, player, active_level_dict): handle_collected_paths(rom) + handle_vertical_scroll(rom) + # Handle Level Shuffle handle_level_shuffle(rom, active_level_dict) From 65995cd5865825c536ddbcf5ca342952ffc17530 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sat, 3 Dec 2022 23:29:33 +0100 Subject: [PATCH 02/35] Network: implement read_only datastore keys: hints and slot_data (#1286) Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com> --- CommonClient.py | 4 +- MultiServer.py | 141 +++++++++++++++++++++++---------------- Utils.py | 2 +- docs/network protocol.md | 78 ++++++++++++++-------- 4 files changed, 139 insertions(+), 86 deletions(-) diff --git a/CommonClient.py b/CommonClient.py index c493462832e0..92761542b93a 100644 --- a/CommonClient.py +++ b/CommonClient.py @@ -134,6 +134,7 @@ class CommonContext: tags: typing.Set[str] = {"AP"} game: typing.Optional[str] = None items_handling: typing.Optional[int] = None + want_slot_data: bool = True # should slot_data be retrieved via Connect # datapackage # Contents in flux until connection to server is made, to download correct data for this multiworld. @@ -309,7 +310,7 @@ async def send_connect(self, **kwargs: typing.Any) -> None: 'cmd': 'Connect', 'password': self.password, 'name': self.auth, 'version': Utils.version_tuple, 'tags': self.tags, 'items_handling': self.items_handling, - 'uuid': Utils.get_unique_identifier(), 'game': self.game + 'uuid': Utils.get_unique_identifier(), 'game': self.game, "slot_data": self.want_slot_data, } if kwargs: payload.update(kwargs) @@ -801,6 +802,7 @@ class TextContext(CommonContext): tags = {"AP", "IgnoreGame", "TextOnly"} game = "" # empty matches any game since 0.3.2 items_handling = 0b111 # receive all items for /received + want_slot_data = False # Can't use game specific slot_data async def server_auth(self, password_requested: bool = False): if password_requested and not self.password: diff --git a/MultiServer.py b/MultiServer.py index 6e82d157a099..f627ad55d821 100644 --- a/MultiServer.py +++ b/MultiServer.py @@ -120,6 +120,7 @@ class Context: groups: typing.Dict[int, typing.Set[int]] save_version = 2 stored_data: typing.Dict[str, object] + read_data: typing.Dict[str, object] stored_data_notification_clients: typing.Dict[str, typing.Set[Client]] item_names: typing.Dict[int, str] = Utils.KeyedDefaultDict(lambda code: f'Unknown item (ID:{code})') @@ -191,6 +192,7 @@ def __init__(self, host: str, port: int, server_password: str, password: str, lo self.random = random.Random() self.stored_data = {} self.stored_data_notification_clients = collections.defaultdict(weakref.WeakSet) + self.read_data = {} # init empty to satisfy linter, I suppose self.gamespackage = {} @@ -342,7 +344,7 @@ def decompress(data: bytes) -> dict: return restricted_loads(zlib.decompress(data[1:])) def _load(self, decoded_obj: dict, use_embedded_server_options: bool): - + self.read_data = {} mdata_ver = decoded_obj["minimum_versions"]["server"] if mdata_ver > Utils.version_tuple: raise RuntimeError(f"Supplied Multidata (.archipelago) requires a server of at least version {mdata_ver}," @@ -359,6 +361,8 @@ def _load(self, decoded_obj: dict, use_embedded_server_options: bool): self.clients[team][player] = [] self.player_names[team, player] = name self.player_name_lookup[name] = team, player + self.read_data[f"hints_{team}_{player}"] = lambda local_team=team, local_player=player: \ + list(self.get_rechecked_hints(local_team, local_player)) self.seed_name = decoded_obj["seed_name"] self.random.seed(self.seed_name) self.connect_names = decoded_obj['connect_names'] @@ -366,6 +370,8 @@ def _load(self, decoded_obj: dict, use_embedded_server_options: bool): self.remote_start_inventory = decoded_obj.get('remote_start_inventory', decoded_obj['remote_items']) self.locations = decoded_obj['locations'] self.slot_data = decoded_obj['slot_data'] + for slot, data in self.slot_data.items(): + self.read_data[f"slot_data_{slot}"] = lambda data=data: data self.er_hint_data = {int(player): {int(address): name for address, name in loc_data.items()} for player, loc_data in decoded_obj["er_hint_data"].items()} @@ -544,12 +550,17 @@ def get_hint_cost(self, slot): return max(0, int(self.hint_cost * 0.01 * len(self.locations[slot]))) return 0 - def recheck_hints(self): - for team, slot in self.hints: - self.hints[team, slot] = { - hint.re_check(self, team) for hint in - self.hints[team, slot] - } + def recheck_hints(self, team: typing.Optional[int] = None, slot: typing.Optional[int] = None): + for hint_team, hint_slot in self.hints: + if (team is None or team == hint_team) and (slot is None or slot == hint_slot): + self.hints[hint_team, hint_slot] = { + hint.re_check(self, hint_team) for hint in + self.hints[hint_team, hint_slot] + } + + def get_rechecked_hints(self, team: int, slot: int): + self.recheck_hints(team, slot) + return self.hints[team, slot] def get_players_package(self): return [NetworkPlayer(t, p, self.get_aliased_name(t, p), n) for (t, p), n in self.player_names.items()] @@ -584,6 +595,44 @@ def get_aliased_name(self, team: int, slot: int): else: return self.player_names[team, slot] + def notify_hints(self, team: int, hints: typing.List[NetUtils.Hint], only_new: bool = False): + """Send and remember hints.""" + if only_new: + hints = [hint for hint in hints if hint not in self.hints[team, hint.finding_player]] + if not hints: + return + new_hint_events: typing.Set[int] = set() + concerns = collections.defaultdict(list) + for hint in sorted(hints, key=operator.attrgetter('found'), reverse=True): + data = (hint, hint.as_network_message()) + for player in self.slot_set(hint.receiving_player): + concerns[player].append(data) + if not hint.local and data not in concerns[hint.finding_player]: + concerns[hint.finding_player].append(data) + # remember hints in all cases + if not hint.found: + # since hints are bidirectional, finding player and receiving player, + # we can check once if hint already exists + if hint not in self.hints[team, hint.finding_player]: + self.hints[team, hint.finding_player].add(hint) + new_hint_events.add(hint.finding_player) + for player in self.slot_set(hint.receiving_player): + self.hints[team, player].add(hint) + new_hint_events.add(player) + + logging.info("Notice (Team #%d): %s" % (team + 1, format_hint(self, team, hint))) + for slot in new_hint_events: + self.on_new_hint(team, slot) + for slot, hint_data in concerns.items(): + clients = self.clients[team].get(slot) + if not clients: + continue + client_hints = [datum[1] for datum in sorted(hint_data, key=lambda x: x[0].finding_player == slot)] + for client in clients: + async_start(self.send_msgs(client, client_hints)) + + # "events" + def on_goal_achieved(self, client: Client): finished_msg = f'{self.get_aliased_name(client.team, client.slot)} (Team #{client.team + 1})' \ f' has completed their goal.' @@ -596,38 +645,11 @@ def on_goal_achieved(self, client: Client): forfeit_player(self, client.team, client.slot) self.save() # save goal completion flag - -def notify_hints(ctx: Context, team: int, hints: typing.List[NetUtils.Hint], only_new: bool = False): - """Send and remember hints.""" - if only_new: - hints = [hint for hint in hints if hint not in ctx.hints[team, hint.finding_player]] - if not hints: - return - concerns = collections.defaultdict(list) - for hint in sorted(hints, key=operator.attrgetter('found'), reverse=True): - data = (hint, hint.as_network_message()) - for player in ctx.slot_set(hint.receiving_player): - concerns[player].append(data) - if not hint.local and data not in concerns[hint.finding_player]: - concerns[hint.finding_player].append(data) - # remember hints in all cases - if not hint.found: - # since hints are bidirectional, finding player and receiving player, - # we can check once if hint already exists - if hint not in ctx.hints[team, hint.finding_player]: - ctx.hints[team, hint.finding_player].add(hint) - for player in ctx.slot_set(hint.receiving_player): - ctx.hints[team, player].add(hint) - - logging.info("Notice (Team #%d): %s" % (team + 1, format_hint(ctx, team, hint))) - - for slot, hint_data in concerns.items(): - clients = ctx.clients[team].get(slot) - if not clients: - continue - client_hints = [datum[1] for datum in sorted(hint_data, key=lambda x: x[0].finding_player == slot)] - for client in clients: - async_start(ctx.send_msgs(client, client_hints)) + def on_new_hint(self, team: int, slot: int): + key: str = f"_read_hints_{team}_{slot}" + targets: typing.Set[Client] = set(self.stored_data_notification_clients[key]) + if targets: + self.broadcast(targets, [{"cmd": "SetReply", "key": key, "value": self.hints[team, slot]}]) def update_aliases(ctx: Context, team: int): @@ -1133,13 +1155,15 @@ def _cmd_admin(self, command: str = ""): output = f"!admin {command}" if output.lower().startswith( - "!admin login"): # disallow others from seeing the supplied password, whether or not it is correct. + "!admin login"): # disallow others from seeing the supplied password, whether it is correct. output = f"!admin login {('*' * random.randint(4, 16))}" elif output.lower().startswith( - "!admin /option server_password"): # disallow others from knowing what the new remote administration password is. + # disallow others from knowing what the new remote administration password is. + "!admin /option server_password"): output = f"!admin /option server_password {('*' * random.randint(4, 16))}" + # Otherwise notify the others what is happening. self.ctx.notify_all(self.ctx.get_aliased_name(self.client.team, - self.client.slot) + ': ' + output) # Otherwise notify the others what is happening. + self.client.slot) + ': ' + output) if not self.ctx.server_password: self.output("Sorry, Remote administration is disabled") @@ -1147,8 +1171,8 @@ def _cmd_admin(self, command: str = ""): if not command: if self.is_authenticated(): - self.output( - "Usage: !admin [Server command].\nUse !admin /help for help.\nUse !admin logout to log out of the current session.") + self.output("Usage: !admin [Server command].\nUse !admin /help for help.\n" + "Use !admin logout to log out of the current session.") else: self.output("Usage: !admin login [password]") return True @@ -1338,7 +1362,7 @@ def get_hints(self, input_text: str, for_location: bool = False) -> bool: hints = {hint.re_check(self.ctx, self.client.team) for hint in self.ctx.hints[self.client.team, self.client.slot]} self.ctx.hints[self.client.team, self.client.slot] = hints - notify_hints(self.ctx, self.client.team, list(hints)) + self.ctx.notify_hints(self.client.team, list(hints)) self.output(f"A hint costs {self.ctx.get_hint_cost(self.client.slot)} points. " f"You have {points_available} points.") return True @@ -1391,7 +1415,7 @@ def get_hints(self, input_text: str, for_location: bool = False) -> bool: new_hints = set(hints) - self.ctx.hints[self.client.team, self.client.slot] old_hints = set(hints) - new_hints if old_hints: - notify_hints(self.ctx, self.client.team, list(old_hints)) + self.ctx.notify_hints(self.client.team, list(old_hints)) if not new_hints: self.output("Hint was previously used, no points deducted.") if new_hints: @@ -1432,7 +1456,7 @@ def get_hints(self, input_text: str, for_location: bool = False) -> bool: self.output(f"You can't afford the hint. " f"You have {points_available} points and need at least " f"{self.ctx.get_hint_cost(self.client.slot)}.") - notify_hints(self.ctx, self.client.team, hints) + self.ctx.notify_hints(self.client.team, hints) self.ctx.save() return True @@ -1554,15 +1578,15 @@ async def process_client_cmd(ctx: Context, client: Client, args: dict): client.version = args['version'] client.tags = args['tags'] client.no_locations = 'TextOnly' in client.tags or 'Tracker' in client.tags - reply = [{ + connected_packet = { "cmd": "Connected", "team": client.team, "slot": client.slot, "players": ctx.get_players_package(), "missing_locations": get_missing_checks(ctx, team, slot), "checked_locations": get_checked_checks(ctx, team, slot), - "slot_data": ctx.slot_data[client.slot], "slot_info": ctx.slot_info - }] + } + reply = [connected_packet] start_inventory = get_start_inventory(ctx, slot, client.remote_start_inventory) items = get_received_items(ctx, client.team, client.slot, client.remote_items) if (start_inventory or items) and not client.no_items: @@ -1571,7 +1595,8 @@ async def process_client_cmd(ctx: Context, client: Client, args: dict): if not client.auth: # if this was a Re-Connect, don't print to console client.auth = True await on_client_joined(ctx, client) - + if args.get("slot_data", True): + connected_packet["slot_data"] = ctx.slot_data[client.slot] await ctx.send_msgs(client, reply) elif cmd == "GetDataPackage": @@ -1659,7 +1684,7 @@ async def process_client_cmd(ctx: Context, client: Client, args: dict): if create_as_hint: hints.extend(collect_hint_location_id(ctx, client.team, client.slot, location)) locs.append(NetworkItem(target_item, location, target_player, flags)) - notify_hints(ctx, client.team, hints, only_new=create_as_hint == 2) + ctx.notify_hints(client.team, hints, only_new=create_as_hint == 2) await ctx.send_msgs(client, [{'cmd': 'LocationInfo', 'locations': locs}]) elif cmd == 'StatusUpdate': @@ -1693,11 +1718,15 @@ async def process_client_cmd(ctx: Context, client: Client, args: dict): return args["cmd"] = "Retrieved" keys = args["keys"] - args["keys"] = {key: ctx.stored_data.get(key, None) for key in keys} + args["keys"] = { + key: ctx.read_data.get(key[6:], lambda: None)() if key.startswith("_read_") else + ctx.stored_data.get(key, None) + for key in keys + } await ctx.send_msgs(client, [args]) elif cmd == "Set": - if "key" not in args or \ + if "key" not in args or args["key"].startswith("_read_") or \ "operations" not in args or not type(args["operations"]) == list: await ctx.send_msgs(client, [{'cmd': 'InvalidPacket', "type": "arguments", "text": 'Set', "original_cmd": cmd}]) @@ -1962,7 +1991,7 @@ def _cmd_hint(self, player_name: str, *item_name: str) -> bool: hints = collect_hints(self.ctx, team, slot, item) if hints: - notify_hints(self.ctx, team, hints) + self.ctx.notify_hints(team, hints) else: self.output("No hints found.") @@ -1997,7 +2026,7 @@ def _cmd_hint_location(self, player_name: str, *location_name: str) -> bool: else: hints = collect_hint_location_name(self.ctx, team, slot, location) if hints: - notify_hints(self.ctx, team, hints) + self.ctx.notify_hints(team, hints) else: self.output("No hints found.") return True diff --git a/Utils.py b/Utils.py index e7833f9a22ec..592207807c69 100644 --- a/Utils.py +++ b/Utils.py @@ -677,7 +677,7 @@ def read_snes_rom(stream: BinaryIO, strip_header: bool = True) -> bytearray: _faf_tasks: "Set[asyncio.Task[None]]" = set() -def async_start(co: Coroutine[None, None, None], name: Optional[str] = None) -> None: +def async_start(co: Coroutine[typing.Any, typing.Any, bool], name: Optional[str] = None) -> None: """ Use this to start a task when you don't keep a reference to it or immediately await it, to prevent early garbage collection. "fire-and-forget" diff --git a/docs/network protocol.md b/docs/network protocol.md index c0a076488165..c870d2f6edbe 100644 --- a/docs/network protocol.md +++ b/docs/network protocol.md @@ -121,15 +121,15 @@ InvalidItemsHandling indicates a wrong value type or flag combination was sent. ### Connected Sent to clients when the connection handshake is successfully completed. #### Arguments -| Name | Type | Notes | -| ---- | ---- | ----- | -| team | int | Your team number. See [NetworkPlayer](#NetworkPlayer) for more info on team number. | -| slot | int | Your slot number on your team. See [NetworkPlayer](#NetworkPlayer) for more info on the slot number. | -| players | list\[[NetworkPlayer](#NetworkPlayer)\] | List denoting other players in the multiworld, whether connected or not. | -| missing_locations | list\[int\] | Contains ids of remaining locations that need to be checked. Useful for trackers, among other things. | -| checked_locations | list\[int\] | Contains ids of all locations that have been checked. Useful for trackers, among other things. Location ids are in the range of ± 253-1. | -| slot_data | dict | Contains a json object for slot related data, differs per game. Empty if not required. | -| slot_info | dict\[int, [NetworkSlot](#NetworkSlot)\] | maps each slot to a [NetworkSlot](#NetworkSlot) information | +| Name | Type | Notes | +|-------------------|------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------| +| team | int | Your team number. See [NetworkPlayer](#NetworkPlayer) for more info on team number. | +| slot | int | Your slot number on your team. See [NetworkPlayer](#NetworkPlayer) for more info on the slot number. | +| players | list\[[NetworkPlayer](#NetworkPlayer)\] | List denoting other players in the multiworld, whether connected or not. | +| missing_locations | list\[int\] | Contains ids of remaining locations that need to be checked. Useful for trackers, among other things. | +| checked_locations | list\[int\] | Contains ids of all locations that have been checked. Useful for trackers, among other things. Location ids are in the range of ± 253-1. | +| slot_data | dict | Contains a json object for slot related data, differs per game. Empty if not required. Not present if slot_data in [Connect](#Connect) is false. | +| slot_info | dict\[int, [NetworkSlot](#NetworkSlot)\] | maps each slot to a [NetworkSlot](#NetworkSlot) information | ### ReceivedItems Sent to clients when they receive an item. @@ -242,11 +242,11 @@ Additional arguments added to the [Get](#Get) package that triggered this [Retri ### SetReply Sent to clients in response to a [Set](#Set) package if want_reply was set to true, or if the client has registered to receive updates for a certain key using the [SetNotify](#SetNotify) package. SetReply packages are sent even if a [Set](#Set) package did not alter the value for the key. #### Arguments -| Name | Type | Notes | -| ---- | ---- | ----- | -| key | str | The key that was updated. | -| value | any | The new value for the key. | -| original_value | any | The value the key had before it was updated. | +| Name | Type | Notes | +|----------------|------|--------------------------------------------------------------------------------------------| +| key | str | The key that was updated. | +| value | any | The new value for the key. | +| original_value | any | The value the key had before it was updated. Not present on "_read" prefixed special keys. | Additional arguments added to the [Set](#Set) package that triggered this [SetReply](#SetReply) will also be passed along. @@ -269,15 +269,16 @@ These packets are sent purely from client to server. They are not accepted by cl Sent by the client to initiate a connection to an Archipelago game session. #### Arguments -| Name | Type | Notes | -| ---- | ---- | ----- | -| password | str | If the game session requires a password, it should be passed here. | -| game | str | The name of the game the client is playing. Example: `A Link to the Past` | -| name | str | The player name for this client. | -| uuid | str | Unique identifier for player client. | -| version | [NetworkVersion](#NetworkVersion) | An object representing the Archipelago version this client supports. | -| items_handling | int | Flags configuring which items should be sent by the server. Read below for individual flags. | -| tags | list\[str\] | Denotes special features or capabilities that the sender is capable of. [Tags](#Tags) | +| Name | Type | Notes | +|----------------|-----------------------------------|----------------------------------------------------------------------------------------------| +| password | str | If the game session requires a password, it should be passed here. | +| game | str | The name of the game the client is playing. Example: `A Link to the Past` | +| name | str | The player name for this client. | +| uuid | str | Unique identifier for player client. | +| version | [NetworkVersion](#NetworkVersion) | An object representing the Archipelago version this client supports. | +| items_handling | int | Flags configuring which items should be sent by the server. Read below for individual flags. | +| tags | list\[str\] | Denotes special features or capabilities that the sender is capable of. [Tags](#Tags) | +| slot_data | bool | If true, the Connect answer will contain slot_data | #### items_handling flags | Value | Meaning | @@ -366,15 +367,22 @@ Used to request a single or multiple values from the server's data storage, see | keys | list\[str\] | Keys to retrieve the values for. | Additional arguments sent in this package will also be added to the [Retrieved](#Retrieved) package it triggers. +Some special keys exist with specific return data: + +| Name | Type | Notes | +|----------------------------|-----------------------|----------------------------------------------| +| \_read_hints_{team}_{slot} | list\[[Hint](#Hint)\] | All Hints belonging to the requested Player. | +| \_read_slot_data_{slot} | any | slot_data belonging to the requested slot. | + ### Set Used to write data to the server's data storage, that data can then be shared across worlds or just saved for later. Values for keys in the data storage can be retrieved with a [Get](#Get) package, or monitored with a [SetNotify](#SetNotify) package. #### Arguments -| Name | Type | Notes | -| ------ | ----- | ------ | -| key | str | The key to manipulate. | -| default | any | The default value to use in case the key has no value on the server. | -| want_reply | bool | If true, the server will send a [SetReply](#SetReply) response back to the client. | +| Name | Type | Notes | +|------------|-------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------| +| key | str | The key to manipulate. Can never start with "_read". | +| default | any | The default value to use in case the key has no value on the server. | +| want_reply | bool | If true, the server will send a [SetReply](#SetReply) response back to the client. | | operations | list\[[DataStorageOperation](#DataStorageOperation)\] | Operations to apply to the value, multiple operations can be present and they will be executed in order of appearance. | Additional arguments sent in this package will also be added to the [SetReply](#SetReply) package it triggers. @@ -591,6 +599,20 @@ class Permission(enum.IntEnum): auto_enabled = 0b111 # 7, forces use after goal completion, allows manual use any time ``` +### Hint +An object representing a Hint. +```python +import typing +class Hint(typing.NamedTuple): + receiving_player: int + finding_player: int + location: int + item: int + found: bool + entrance: str = "" + item_flags: int = 0 +``` + ### Data Package Contents A data package is a JSON object which may contain arbitrary metadata to enable a client to interact with the Archipelago server most easily. Currently, this package is used to send ID to name mappings so that clients need not maintain their own mappings. From 203f17f0f6da6d2cdfb4f072bff077947e13b49c Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sat, 3 Dec 2022 00:31:28 +0100 Subject: [PATCH 03/35] Subnautica: prompt users to update --- worlds/subnautica/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/subnautica/__init__.py b/worlds/subnautica/__init__.py index 7f876192deba..33d1f01987e6 100644 --- a/worlds/subnautica/__init__.py +++ b/worlds/subnautica/__init__.py @@ -42,7 +42,7 @@ class SubnauticaWorld(World): option_definitions = Options.options data_version = 7 - required_client_version = (0, 3, 5) + required_client_version = (0, 3, 6) creatures_to_scan: List[str] From 38b5a90c07af54d14e17b52dc51cae71cf7bd4a5 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Tue, 29 Nov 2022 21:21:04 +0100 Subject: [PATCH 04/35] WebHost: update modules --- WebHostLib/requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WebHostLib/requirements.txt b/WebHostLib/requirements.txt index 89972531171b..2397bf91b46e 100644 --- a/WebHostLib/requirements.txt +++ b/WebHostLib/requirements.txt @@ -3,5 +3,5 @@ pony>=0.7.16 waitress>=2.1.2 Flask-Caching>=2.0.1 Flask-Compress>=1.13 -Flask-Limiter>=2.7.0 -bokeh>=3.0.0 +Flask-Limiter>=2.8.1 +bokeh>=3.0.2 From 679cb3e197aa5c6def04cce82476ee8c26179f34 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Mon, 21 Nov 2022 19:30:45 +0100 Subject: [PATCH 05/35] Factorio: fix revealed tech tree hinting old location names --- worlds/factorio/__init__.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/worlds/factorio/__init__.py b/worlds/factorio/__init__.py index 71103e7cd589..0053a016e372 100644 --- a/worlds/factorio/__init__.py +++ b/worlds/factorio/__init__.py @@ -179,6 +179,10 @@ def generate_basic(self): "logistics": 1, "rocket-silo": -1} loc: FactorioScienceLocation + if self.multiworld.tech_tree_information[player] == TechTreeInformation.option_full: + # mark all locations as pre-hinted + for loc in self.locations: + loc.revealed = True if self.skip_silo: removed = useless_technologies | {"rocket-silo"} else: @@ -201,11 +205,15 @@ def generate_basic(self): if map_basic_settings.get("seed", None) is None: # allow seed 0 map_basic_settings["seed"] = self.multiworld.slot_seeds[player].randint(0, 2 ** 32 - 1) # 32 bit uint - if self.multiworld.tech_tree_information[player] == TechTreeInformation.option_full: - # mark all locations as pre-hinted - self.multiworld.start_location_hints[self.player].value.update(base_tech_table) - for loc in self.locations: + start_location_hints: typing.Set[str] = self.multiworld.start_location_hints[self.player].value + + for loc in self.locations: + # show start_location_hints ingame + if loc.name in start_location_hints: loc.revealed = True + # make spoiler match mod info + elif loc.revealed: + start_location_hints.add(loc.name) def collect_item(self, state, item, remove=False): if item.advancement and item.name in progressive_technology_table: From 63f012cce747f25a562622990d2381dded9fbe52 Mon Sep 17 00:00:00 2001 From: Jarno Date: Sun, 4 Dec 2022 22:02:46 +0100 Subject: [PATCH 06/35] Timespinner: Enter Sandman flag (#1263) --- worlds/timespinner/Locations.py | 4 ++-- worlds/timespinner/Options.py | 24 ++++++------------------ worlds/timespinner/Regions.py | 8 ++++++-- 3 files changed, 14 insertions(+), 22 deletions(-) diff --git a/worlds/timespinner/Locations.py b/worlds/timespinner/Locations.py index b881cf3532aa..bdc3e380075d 100644 --- a/worlds/timespinner/Locations.py +++ b/worlds/timespinner/Locations.py @@ -186,12 +186,12 @@ def get_locations(world: Optional[MultiWorld], player: Optional[int]) -> Tuple[L LocationData('Royal towers (upper)', 'Royal Towers: Aelana\'s pedestal', 1337155), # Ancient pyramid locations - LocationData('Ancient Pyramid (left)', 'Ancient Pyramid: Why not it\'s right there', 1337246), + LocationData('Ancient Pyramid (entrance)', 'Ancient Pyramid: Why not it\'s right there', 1337246), LocationData('Ancient Pyramid (left)', 'Ancient Pyramid: Conviction guarded room', 1337247), LocationData('Ancient Pyramid (left)', 'Ancient Pyramid: Pit secret room', 1337248, lambda state: state._timespinner_can_break_walls(world, player)), LocationData('Ancient Pyramid (left)', 'Ancient Pyramid: Regret chest', 1337249, lambda state: state._timespinner_can_break_walls(world, player)), LocationData('Ancient Pyramid (right)', 'Ancient Pyramid: Nightmare Door chest', 1337236), - LocationData('Ancient Pyramid (right)', 'Killed Nightmare', EventId) + LocationData('Ancient Pyramid (right)', 'Killed Nightmare', EventId, lambda state: state.has_all({'Timespinner Wheel', 'Timespinner Spindle', 'Timespinner Gear 1', 'Timespinner Gear 2', 'Timespinner Gear 3'}, player)) ] # 1337156 - 1337170 Downloads diff --git a/worlds/timespinner/Options.py b/worlds/timespinner/Options.py index 5e0438a13fe0..81c686631729 100644 --- a/worlds/timespinner/Options.py +++ b/worlds/timespinner/Options.py @@ -9,16 +9,6 @@ class StartWithJewelryBox(Toggle): display_name = "Start with Jewelry Box" -#class ProgressiveVerticalMovement(Toggle): -# "Always find vertical movement in the following order Succubus Hairpin -> Light Wall -> Celestial Sash" -# display_name = "Progressive vertical movement" - - -#class ProgressiveKeycards(Toggle): -# "Always find Security Keycard's in the following order D -> C -> B -> A" -# display_name = "Progressive keycards" - - class DownloadableItems(DefaultOnToggle): "With the tablet you will be able to download items at terminals" display_name = "Downloadable items" @@ -49,11 +39,6 @@ class Inverted(Toggle): display_name = "Inverted" -#class StinkyMaw(Toggle): -# "Require gasmask for Maw" -# display_name = "Stinky Maw" - - class GyreArchives(Toggle): "Gyre locations are in logic. New warps are gated by Merchant Crow and Kobo" display_name = "Gyre Archives" @@ -292,18 +277,20 @@ class ShowDrops(Toggle): display_name = "Show Bestiary Item Drops" +class EnterSandman(Toggle): + "The Ancient Pyramid is unlocked by the Twin Pyramid Keys, but the final boss door opens if you have all 5 Timespinner pieces" + display_name = "Enter Sandman" + + # Some options that are available in the timespinner randomizer arent currently implemented timespinner_options: Dict[str, Option] = { "StartWithJewelryBox": StartWithJewelryBox, - #"ProgressiveVerticalMovement": ProgressiveVerticalMovement, - #"ProgressiveKeycards": ProgressiveKeycards, "DownloadableItems": DownloadableItems, "EyeSpy": EyeSpy, "StartWithMeyef": StartWithMeyef, "QuickSeed": QuickSeed, "SpecificKeycards": SpecificKeycards, "Inverted": Inverted, - #"StinkyMaw": StinkyMaw, "GyreArchives": GyreArchives, "Cantoran": Cantoran, "LoreChecks": LoreChecks, @@ -322,6 +309,7 @@ class ShowDrops(Toggle): "LootTierDistro": LootTierDistro, "ShowBestiary": ShowBestiary, "ShowDrops": ShowDrops, + "EnterSandman": EnterSandman, "DeathLink": DeathLink, } diff --git a/worlds/timespinner/Regions.py b/worlds/timespinner/Regions.py index cd9c6b4c8112..ed0a30d38927 100644 --- a/worlds/timespinner/Regions.py +++ b/worlds/timespinner/Regions.py @@ -46,6 +46,7 @@ def create_regions(world: MultiWorld, player: int, locations: Tuple[LocationData create_region(world, player, locations_per_region, location_cache, 'Royal towers'), create_region(world, player, locations_per_region, location_cache, 'Royal towers (upper)'), create_region(world, player, locations_per_region, location_cache, 'Temporal Gyre'), + create_region(world, player, locations_per_region, location_cache, 'Ancient Pyramid (entrance)'), create_region(world, player, locations_per_region, location_cache, 'Ancient Pyramid (left)'), create_region(world, player, locations_per_region, location_cache, 'Ancient Pyramid (right)'), create_region(world, player, locations_per_region, location_cache, 'Space time continuum') @@ -109,7 +110,7 @@ def create_regions(world: MultiWorld, player: int, locations: Tuple[LocationData connect(world, player, names, 'The lab (upper)', 'The lab (power off)') connect(world, player, names, 'The lab (upper)', 'Ravenlord\'s Lair', lambda state: state.has('Merchant Crow', player)) connect(world, player, names, 'The lab (upper)', 'Emperors tower', lambda state: state._timespinner_has_forwarddash_doublejump(world, player)) - connect(world, player, names, 'The lab (upper)', 'Ancient Pyramid (left)', lambda state: state.has_all({'Timespinner Wheel', 'Timespinner Spindle', 'Timespinner Gear 1', 'Timespinner Gear 2', 'Timespinner Gear 3'}, player)) + connect(world, player, names, 'The lab (upper)', 'Ancient Pyramid (entrance)', lambda state: state.has_all({'Timespinner Wheel', 'Timespinner Spindle', 'Timespinner Gear 1', 'Timespinner Gear 2', 'Timespinner Gear 3'}, player)) connect(world, player, names, 'Ravenlord\'s Lair', 'The lab (upper)') connect(world, player, names, 'Emperors tower', 'The lab (upper)') connect(world, player, names, 'Skeleton Shaft', 'Lake desolation') @@ -154,7 +155,9 @@ def create_regions(world: MultiWorld, player: int, locations: Tuple[LocationData connect(world, player, names, 'Royal towers', 'Royal towers (lower)') connect(world, player, names, 'Royal towers', 'Royal towers (upper)', lambda state: state._timespinner_has_doublejump(world, player)) connect(world, player, names, 'Royal towers (upper)', 'Royal towers') - connect(world, player, names, 'Ancient Pyramid (left)', 'The lab (upper)') + connect(world, player, names, 'Ancient Pyramid (entrance)', 'The lab (upper)', lambda state: not is_option_enabled(world, player, "EnterSandman")) + connect(world, player, names, 'Ancient Pyramid (entrance)', 'Ancient Pyramid (left)', lambda state: state._timespinner_has_doublejump(world, player)) + connect(world, player, names, 'Ancient Pyramid (left)', 'Ancient Pyramid (entrance)') connect(world, player, names, 'Ancient Pyramid (left)', 'Ancient Pyramid (right)', lambda state: state._timespinner_has_upwarddash(world, player)) connect(world, player, names, 'Ancient Pyramid (right)', 'Ancient Pyramid (left)', lambda state: state._timespinner_has_upwarddash(world, player)) connect(world, player, names, 'Space time continuum', 'Lake desolation', lambda state: pyramid_keys_unlock == "GateLakeDesolation") @@ -170,6 +173,7 @@ def create_regions(world: MultiWorld, player: int, locations: Tuple[LocationData connect(world, player, names, 'Space time continuum', 'Royal towers (lower)', lambda state: pyramid_keys_unlock == "GateRoyalTowers") connect(world, player, names, 'Space time continuum', 'Caves of Banishment (Maw)', lambda state: pyramid_keys_unlock == "GateMaw") connect(world, player, names, 'Space time continuum', 'Caves of Banishment (upper)', lambda state: pyramid_keys_unlock == "GateCavesOfBanishment") + connect(world, player, names, 'Space time continuum', 'Ancient Pyramid (entrance)', lambda state: is_option_enabled(world, player, "EnterSandman")) def throwIfAnyLocationIsNotAssignedToARegion(regions: List[Region], regionNames: Set[str]): From 6b57275859296141f1074e2ad7be92e69b15ecd9 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Mon, 5 Dec 2022 02:06:13 +0100 Subject: [PATCH 07/35] Server: attempt to make nothing found message clearer (#1289) --- MultiServer.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/MultiServer.py b/MultiServer.py index f627ad55d821..ad134901b980 100644 --- a/MultiServer.py +++ b/MultiServer.py @@ -1462,7 +1462,12 @@ def get_hints(self, input_text: str, for_location: bool = False) -> bool: else: if points_available >= cost: - self.output("Nothing found. Item/Location may not exist.") + if for_location: + self.output(f"Nothing found for recognized location name \"{hint_name}\". " + f"Location appears to not exist in this multiworld.") + else: + self.output(f"Nothing found for recognized item name \"{hint_name}\". " + f"Item appears to not exist in this multiworld.") else: self.output(f"You can't afford the hint. " f"You have {points_available} points and need at least " From d81fd280fa5f7a85ea5d60d3bbaa98268d4e64af Mon Sep 17 00:00:00 2001 From: kindasneaki Date: Sun, 4 Dec 2022 18:16:03 -0700 Subject: [PATCH 08/35] RoR2: Change risk of rain mod download page link (#1269) --- worlds/ror2/docs/setup_en.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/ror2/docs/setup_en.md b/worlds/ror2/docs/setup_en.md index e0088e47da79..ac80120b779a 100644 --- a/worlds/ror2/docs/setup_en.md +++ b/worlds/ror2/docs/setup_en.md @@ -12,7 +12,7 @@ Head on over to the r2modman page on Thunderstore and follow the installation in You can install the Archipelago mod using r2modman in one of two ways. -[Archipelago Mod Download Page](https://thunderstore.io/package/ArchipelagoMW/Archipelago/) +[Archipelago Mod Download Page](https://thunderstore.io/package/Sneaki/Archipelago/) One, you can use the Thunderstore website and click on the "Install with Mod Manager" link. From 45719eb7e010625a80d9e92d51e36864ee8fa264 Mon Sep 17 00:00:00 2001 From: BordynConfused Date: Sun, 4 Dec 2022 19:25:16 -0600 Subject: [PATCH 09/35] Hylics2:Logic Fixes (#1281) Added Juice Ranch and Worm Pod to logic Extra parenthesis removed * Delete log.txt Not used Transitioned all whitespace characters to Linux /n from Windows /r/n Updated rules for Rescue Blerol 1 and 2 to avoid impossible seed generation. Fixed member requirements for Vault Bomb. Added Juice Ranch: Fridge for consistency with other checks behind combat. Added party_shuffle rules to vault medallions, removed jail key requirement (not needed) --- worlds/hylics2/Rules.py | 236 ++++++++++++++++++++-------------------- 1 file changed, 118 insertions(+), 118 deletions(-) diff --git a/worlds/hylics2/Rules.py b/worlds/hylics2/Rules.py index 4b804386d368..2fd016922746 100644 --- a/worlds/hylics2/Rules.py +++ b/worlds/hylics2/Rules.py @@ -27,7 +27,7 @@ def _hylics2_has_upper_chamber_key(self, player): def _hylics2_has_vessel_room_key(self, player): return self.has("VESSEL ROOM KEY", player) - + def _hylics2_has_house_key(self, player): return self.has("HOUSE KEY", player) @@ -84,7 +84,7 @@ def _hylics2_enter_foglast(self, player): return self._hylics2_enter_wormpod(player) def _hylics2_enter_hylemxylem(self, player): - return self._hylics2_can_air_dash(player) and self._hylics2_enter_wormpod(player) and\ + return self._hylics2_can_air_dash(player) and self._hylics2_enter_foglast(player) and\ self._hylics2_has_bridge_key(player) @@ -93,167 +93,167 @@ def set_rules(hylics2world): player = hylics2world.player # Afterlife - add_rule(world.get_location("Afterlife: TV", player), + add_rule(world.get_location("Afterlife: TV", player), lambda state: state._hylics2_has_cave_key(player)) # New Muldul - add_rule(world.get_location("New Muldul: Underground Chest", player), + add_rule(world.get_location("New Muldul: Underground Chest", player), lambda state: state._hylics2_can_air_dash(player)) add_rule(world.get_location("New Muldul: TV", player), lambda state: state._hylics2_has_house_key(player)) - add_rule(world.get_location("New Muldul: Upper House Chest 1", player), + add_rule(world.get_location("New Muldul: Upper House Chest 1", player), lambda state: state._hylics2_has_upper_house_key(player)) - add_rule(world.get_location("New Muldul: Upper House Chest 2", player), + add_rule(world.get_location("New Muldul: Upper House Chest 2", player), lambda state: state._hylics2_has_upper_house_key(player)) # New Muldul Vault - add_rule(world.get_location("New Muldul: Rescued Blerol 1", player), - lambda state: (state._hylics2_can_air_dash(player) or state._hylics2_has_airship(player)) and\ - ((state._hylics2_has_jail_key(player) and state._hylics2_has_paddle(player)) or\ - (state._hylics2_has_bridge_key(player) and state._hylics2_has_worm_room_key(player)))) - add_rule(world.get_location("New Muldul: Rescued Blerol 2", player), - lambda state: (state._hylics2_can_air_dash(player) or state._hylics2_has_airship(player)) and\ - ((state._hylics2_has_jail_key(player) and state._hylics2_has_paddle(player)) or\ - (state._hylics2_has_bridge_key(player) and state._hylics2_has_worm_room_key(player)))) - add_rule(world.get_location("New Muldul: Vault Left Chest", player), - lambda state: state._hylics2_enter_foglast(player) and state._hylics2_has_bridge_key(player)) - add_rule(world.get_location("New Muldul: Vault Right Chest", player), - lambda state: state._hylics2_enter_foglast(player) and state._hylics2_has_bridge_key(player)) - add_rule(world.get_location("New Muldul: Vault Bomb", player), - lambda state: state._hylics2_enter_foglast(player) and state._hylics2_has_bridge_key(player)) + add_rule(world.get_location("New Muldul: Rescued Blerol 1", player), + lambda state: ((state._hylics2_has_jail_key(player) and state._hylics2_has_paddle(player)) and\ + (state._hylics2_can_air_dash(player) or state._hylics2_has_airship(player))) or\ + state._hylics2_enter_hylemxylem(player)) + add_rule(world.get_location("New Muldul: Rescued Blerol 2", player), + lambda state: ((state._hylics2_has_jail_key(player) and state._hylics2_has_paddle(player)) and\ + (state._hylics2_can_air_dash(player) or state._hylics2_has_airship(player))) or\ + state._hylics2_enter_hylemxylem(player)) + add_rule(world.get_location("New Muldul: Vault Left Chest", player), + lambda state: state._hylics2_enter_hylemxylem(player)) + add_rule(world.get_location("New Muldul: Vault Right Chest", player), + lambda state: state._hylics2_enter_hylemxylem(player)) + add_rule(world.get_location("New Muldul: Vault Bomb", player), + lambda state: state._hylics2_enter_hylemxylem(player)) # Viewax's Edifice - add_rule(world.get_location("Viewax's Edifice: Canopic Jar", player), + add_rule(world.get_location("Viewax's Edifice: Canopic Jar", player), lambda state: state._hylics2_has_paddle(player)) - add_rule(world.get_location("Viewax's Edifice: Cave Sarcophagus", player), + add_rule(world.get_location("Viewax's Edifice: Cave Sarcophagus", player), lambda state: state._hylics2_has_paddle(player)) - add_rule(world.get_location("Viewax's Edifice: Shielded Key", player), + add_rule(world.get_location("Viewax's Edifice: Shielded Key", player), lambda state: state._hylics2_has_paddle(player)) - add_rule(world.get_location("Viewax's Edifice: Shielded Key", player), + add_rule(world.get_location("Viewax's Edifice: Shielded Key", player), lambda state: state._hylics2_has_paddle(player)) - add_rule(world.get_location("Viewax's Edifice: Tower Pot", player), + add_rule(world.get_location("Viewax's Edifice: Tower Pot", player), lambda state: state._hylics2_has_paddle(player)) - add_rule(world.get_location("Viewax's Edifice: Tower Jar", player), + add_rule(world.get_location("Viewax's Edifice: Tower Jar", player), lambda state: state._hylics2_has_paddle(player)) - add_rule(world.get_location("Viewax's Edifice: Tower Chest", player), + add_rule(world.get_location("Viewax's Edifice: Tower Chest", player), lambda state: state._hylics2_has_paddle(player) and state._hylics2_has_tower_key(player)) - add_rule(world.get_location("Viewax's Edifice: Viewax Pot", player), + add_rule(world.get_location("Viewax's Edifice: Viewax Pot", player), lambda state: state._hylics2_has_paddle(player)) - add_rule(world.get_location("Viewax's Edifice: Defeat Viewax", player), + add_rule(world.get_location("Viewax's Edifice: Defeat Viewax", player), lambda state: state._hylics2_has_paddle(player)) - add_rule(world.get_location("Viewax's Edifice: TV", player), + add_rule(world.get_location("Viewax's Edifice: TV", player), lambda state: state._hylics2_has_paddle(player) and state._hylics2_has_jail_key(player)) - add_rule(world.get_location("Viewax's Edifice: Sage Fridge", player), + add_rule(world.get_location("Viewax's Edifice: Sage Fridge", player), lambda state: state._hylics2_can_air_dash(player)) - add_rule(world.get_location("Viewax's Edifice: Sage Item 1", player), + add_rule(world.get_location("Viewax's Edifice: Sage Item 1", player), lambda state: state._hylics2_can_air_dash(player)) - add_rule(world.get_location("Viewax's Edifice: Sage Item 2", player), + add_rule(world.get_location("Viewax's Edifice: Sage Item 2", player), lambda state: state._hylics2_can_air_dash(player)) # Arcade 1 - add_rule(world.get_location("Arcade 1: Key", player), + add_rule(world.get_location("Arcade 1: Key", player), lambda state: state._hylics2_has_paddle(player)) - add_rule(world.get_location("Arcade 1: Coin Dash", player), + add_rule(world.get_location("Arcade 1: Coin Dash", player), lambda state: state._hylics2_has_paddle(player)) - add_rule(world.get_location("Arcade 1: Burrito Alcove 1", player), + add_rule(world.get_location("Arcade 1: Burrito Alcove 1", player), lambda state: state._hylics2_has_paddle(player)) - add_rule(world.get_location("Arcade 1: Burrito Alcove 2", player), + add_rule(world.get_location("Arcade 1: Burrito Alcove 2", player), lambda state: state._hylics2_has_paddle(player)) - add_rule(world.get_location("Arcade 1: Behind Spikes Banana", player), + add_rule(world.get_location("Arcade 1: Behind Spikes Banana", player), lambda state: state._hylics2_has_paddle(player)) - add_rule(world.get_location("Arcade 1: Pyramid Banana", player), + add_rule(world.get_location("Arcade 1: Pyramid Banana", player), lambda state: state._hylics2_has_paddle(player)) - add_rule(world.get_location("Arcade 1: Moving Platforms Muscle Applique", player), + add_rule(world.get_location("Arcade 1: Moving Platforms Muscle Applique", player), lambda state: state._hylics2_has_paddle(player)) - add_rule(world.get_location("Arcade 1: Bed Banana", player), + add_rule(world.get_location("Arcade 1: Bed Banana", player), lambda state: state._hylics2_has_paddle(player)) # Airship - add_rule(world.get_location("Airship: Talk to Somsnosa", player), + add_rule(world.get_location("Airship: Talk to Somsnosa", player), lambda state: state._hylics2_has_worm_room_key(player)) # Foglast - add_rule(world.get_location("Foglast: Underground Sarcophagus", player), + add_rule(world.get_location("Foglast: Underground Sarcophagus", player), lambda state: state._hylics2_can_air_dash(player)) - add_rule(world.get_location("Foglast: Shielded Key", player), + add_rule(world.get_location("Foglast: Shielded Key", player), lambda state: state._hylics2_can_air_dash(player)) - add_rule(world.get_location("Foglast: TV", player), + add_rule(world.get_location("Foglast: TV", player), lambda state: state._hylics2_can_air_dash(player) and state._hylics2_has_clicker(player)) - add_rule(world.get_location("Foglast: Buy Clicker", player), + add_rule(world.get_location("Foglast: Buy Clicker", player), lambda state: state._hylics2_can_air_dash(player)) - add_rule(world.get_location("Foglast: Shielded Chest", player), + add_rule(world.get_location("Foglast: Shielded Chest", player), lambda state: state._hylics2_can_air_dash(player)) - add_rule(world.get_location("Foglast: Cave Fridge", player), + add_rule(world.get_location("Foglast: Cave Fridge", player), lambda state: state._hylics2_can_air_dash(player)) - add_rule(world.get_location("Foglast: Roof Sarcophagus", player), + add_rule(world.get_location("Foglast: Roof Sarcophagus", player), lambda state: state._hylics2_can_air_dash(player) and state._hylics2_has_bridge_key(player)) - add_rule(world.get_location("Foglast: Under Lair Sarcophagus 1", player), + add_rule(world.get_location("Foglast: Under Lair Sarcophagus 1", player), lambda state: state._hylics2_can_air_dash(player) and state._hylics2_has_bridge_key(player)) - add_rule(world.get_location("Foglast: Under Lair Sarcophagus 2", player), + add_rule(world.get_location("Foglast: Under Lair Sarcophagus 2", player), lambda state: state._hylics2_can_air_dash(player) and state._hylics2_has_bridge_key(player)) - add_rule(world.get_location("Foglast: Under Lair Sarcophagus 3", player), + add_rule(world.get_location("Foglast: Under Lair Sarcophagus 3", player), lambda state: state._hylics2_can_air_dash(player) and state._hylics2_has_bridge_key(player)) - add_rule(world.get_location("Foglast: Sage Sarcophagus", player), + add_rule(world.get_location("Foglast: Sage Sarcophagus", player), lambda state: state._hylics2_can_air_dash(player) and state._hylics2_has_bridge_key(player)) - add_rule(world.get_location("Foglast: Sage Item 1", player), + add_rule(world.get_location("Foglast: Sage Item 1", player), lambda state: state._hylics2_can_air_dash(player) and state._hylics2_has_bridge_key(player)) - add_rule(world.get_location("Foglast: Sage Item 2", player), + add_rule(world.get_location("Foglast: Sage Item 2", player), lambda state: state._hylics2_can_air_dash(player) and state._hylics2_has_bridge_key(player)) # Drill Castle - add_rule(world.get_location("Drill Castle: Island Banana", player), + add_rule(world.get_location("Drill Castle: Island Banana", player), lambda state: state._hylics2_can_air_dash(player)) - add_rule(world.get_location("Drill Castle: Island Pot", player), + add_rule(world.get_location("Drill Castle: Island Pot", player), lambda state: state._hylics2_can_air_dash(player)) - add_rule(world.get_location("Drill Castle: Cave Sarcophagus", player), + add_rule(world.get_location("Drill Castle: Cave Sarcophagus", player), lambda state: state._hylics2_can_air_dash(player)) - add_rule(world.get_location("Drill Castle: TV", player), + add_rule(world.get_location("Drill Castle: TV", player), lambda state: state._hylics2_can_air_dash(player)) # Sage Labyrinth - add_rule(world.get_location("Sage Labyrinth: Sage Item 1", player), + add_rule(world.get_location("Sage Labyrinth: Sage Item 1", player), lambda state: state._hylics2_has_deep_key(player)) - add_rule(world.get_location("Sage Labyrinth: Sage Item 2", player), + add_rule(world.get_location("Sage Labyrinth: Sage Item 2", player), lambda state: state._hylics2_has_deep_key(player)) - add_rule(world.get_location("Sage Labyrinth: Sage Left Arm", player), + add_rule(world.get_location("Sage Labyrinth: Sage Left Arm", player), lambda state: state._hylics2_has_deep_key(player)) - add_rule(world.get_location("Sage Labyrinth: Sage Right Arm", player), + add_rule(world.get_location("Sage Labyrinth: Sage Right Arm", player), lambda state: state._hylics2_has_deep_key(player)) - add_rule(world.get_location("Sage Labyrinth: Sage Left Leg", player), + add_rule(world.get_location("Sage Labyrinth: Sage Left Leg", player), lambda state: state._hylics2_has_deep_key(player)) - add_rule(world.get_location("Sage Labyrinth: Sage Right Leg", player), + add_rule(world.get_location("Sage Labyrinth: Sage Right Leg", player), lambda state: state._hylics2_has_deep_key(player)) # Sage Airship - add_rule(world.get_location("Sage Airship: TV", player), + add_rule(world.get_location("Sage Airship: TV", player), lambda state: state._hylics2_has_tokens(player)) # Hylemxylem - add_rule(world.get_location("Hylemxylem: Upper Chamber Banana", player), + add_rule(world.get_location("Hylemxylem: Upper Chamber Banana", player), lambda state: state._hylics2_has_upper_chamber_key(player)) - add_rule(world.get_location("Hylemxylem: Across Upper Reservoir Chest", player), + add_rule(world.get_location("Hylemxylem: Across Upper Reservoir Chest", player), lambda state: state._hylics2_has_upper_chamber_key(player)) - add_rule(world.get_location("Hylemxylem: Drained Lower Reservoir Chest", player), + add_rule(world.get_location("Hylemxylem: Drained Lower Reservoir Chest", player), lambda state: state._hylics2_has_upper_chamber_key(player)) - add_rule(world.get_location("Hylemxylem: Drained Lower Reservoir Burrito 1", player), + add_rule(world.get_location("Hylemxylem: Drained Lower Reservoir Burrito 1", player), lambda state: state._hylics2_has_upper_chamber_key(player)) - add_rule(world.get_location("Hylemxylem: Drained Lower Reservoir Burrito 2", player), + add_rule(world.get_location("Hylemxylem: Drained Lower Reservoir Burrito 2", player), lambda state: state._hylics2_has_upper_chamber_key(player)) - add_rule(world.get_location("Hylemxylem: Lower Reservoir Hole Pot 1", player), + add_rule(world.get_location("Hylemxylem: Lower Reservoir Hole Pot 1", player), lambda state: state._hylics2_has_upper_chamber_key(player)) - add_rule(world.get_location("Hylemxylem: Lower Reservoir Hole Pot 2", player), + add_rule(world.get_location("Hylemxylem: Lower Reservoir Hole Pot 2", player), lambda state: state._hylics2_has_upper_chamber_key(player)) - add_rule(world.get_location("Hylemxylem: Lower Reservoir Hole Pot 3", player), + add_rule(world.get_location("Hylemxylem: Lower Reservoir Hole Pot 3", player), lambda state: state._hylics2_has_upper_chamber_key(player)) - add_rule(world.get_location("Hylemxylem: Lower Reservoir Hole Sarcophagus", player), + add_rule(world.get_location("Hylemxylem: Lower Reservoir Hole Sarcophagus", player), lambda state: state._hylics2_has_upper_chamber_key(player)) - add_rule(world.get_location("Hylemxylem: Drained Upper Reservoir Burrito 1", player), + add_rule(world.get_location("Hylemxylem: Drained Upper Reservoir Burrito 1", player), lambda state: state._hylics2_has_upper_chamber_key(player)) - add_rule(world.get_location("Hylemxylem: Drained Upper Reservoir Burrito 2", player), + add_rule(world.get_location("Hylemxylem: Drained Upper Reservoir Burrito 2", player), lambda state: state._hylics2_has_upper_chamber_key(player)) - add_rule(world.get_location("Hylemxylem: Drained Upper Reservoir Burrito 3", player), + add_rule(world.get_location("Hylemxylem: Drained Upper Reservoir Burrito 3", player), lambda state: state._hylics2_has_upper_chamber_key(player)) - add_rule(world.get_location("Hylemxylem: Upper Reservoir Hole Key", player), + add_rule(world.get_location("Hylemxylem: Upper Reservoir Hole Key", player), lambda state: state._hylics2_has_upper_chamber_key(player)) # extra rules if Extra Items in Logic is enabled @@ -266,7 +266,7 @@ def set_rules(hylics2world): for i in world.get_region("Hylemxylem", player).entrances: add_rule(i, lambda state: state._hylics2_has_charge_up(player) and state._hylics2_has_cup(player)) - add_rule(world.get_location("Sage Labyrinth: Motor Hunter Sarcophagus", player), + add_rule(world.get_location("Sage Labyrinth: Motor Hunter Sarcophagus", player), lambda state: state._hylics2_has_charge_up(player) and state._hylics2_has_cup(player)) # extra rules if Shuffle Party Members is enabled @@ -281,72 +281,72 @@ def set_rules(hylics2world): for i in world.get_region("Hylemxylem", player).entrances: add_rule(i, lambda state: state._hylics2_has_3_members(player)) - add_rule(world.get_location("Viewax's Edifice: Defeat Viewax", player), + add_rule(world.get_location("Viewax's Edifice: Defeat Viewax", player), lambda state: state._hylics2_has_2_members(player)) - add_rule(world.get_location("New Muldul: Rescued Blerol 1", player), + add_rule(world.get_location("New Muldul: Rescued Blerol 1", player), lambda state: state._hylics2_has_2_members(player)) - add_rule(world.get_location("New Muldul: Rescued Blerol 2", player), + add_rule(world.get_location("New Muldul: Rescued Blerol 2", player), lambda state: state._hylics2_has_2_members(player)) - add_rule(world.get_location("New Muldul: Vault Left Chest", player), + add_rule(world.get_location("New Muldul: Vault Left Chest", player), lambda state: state._hylics2_has_3_members(player)) - add_rule(world.get_location("New Muldul: Vault Right Chest", player), + add_rule(world.get_location("New Muldul: Vault Right Chest", player), lambda state: state._hylics2_has_3_members(player)) - add_rule(world.get_location("New Muldul: Vault Bomb", player), - lambda state: state._hylics2_has_2_members(player)) - add_rule(world.get_location("Juice Ranch: Battle with Somsnosa", player), + add_rule(world.get_location("New Muldul: Vault Bomb", player), + lambda state: state._hylics2_has_3_members(player)) + add_rule(world.get_location("Juice Ranch: Battle with Somsnosa", player), lambda state: state._hylics2_has_2_members(player)) - add_rule(world.get_location("Juice Ranch: Somsnosa Joins", player), + add_rule(world.get_location("Juice Ranch: Somsnosa Joins", player), lambda state: state._hylics2_has_2_members(player)) - add_rule(world.get_location("Airship: Talk to Somsnosa", player), + add_rule(world.get_location("Airship: Talk to Somsnosa", player), lambda state: state._hylics2_has_3_members(player)) - add_rule(world.get_location("Sage Labyrinth: Motor Hunter Sarcophagus", player), + add_rule(world.get_location("Sage Labyrinth: Motor Hunter Sarcophagus", player), lambda state: state._hylics2_has_3_members(player)) - + # extra rules if Shuffle Red Medallions is enabled if world.medallion_shuffle[player]: add_rule(world.get_location("New Muldul: Upper House Medallion", player), lambda state: state._hylics2_has_upper_house_key(player)) - add_rule(world.get_location("New Muldul: Vault Rear Left Medallion", player), + add_rule(world.get_location("New Muldul: Vault Rear Left Medallion", player), lambda state: state._hylics2_enter_foglast(player) and state._hylics2_has_bridge_key(player)) - add_rule(world.get_location("New Muldul: Vault Rear Right Medallion", player), + add_rule(world.get_location("New Muldul: Vault Rear Right Medallion", player), lambda state: state._hylics2_enter_foglast(player) and state._hylics2_has_bridge_key(player)) - add_rule(world.get_location("New Muldul: Vault Center Medallion", player), + add_rule(world.get_location("New Muldul: Vault Center Medallion", player), lambda state: state._hylics2_enter_foglast(player) and state._hylics2_has_bridge_key(player)) - add_rule(world.get_location("New Muldul: Vault Front Left Medallion", player), + add_rule(world.get_location("New Muldul: Vault Front Left Medallion", player), lambda state: state._hylics2_enter_foglast(player) and state._hylics2_has_bridge_key(player)) - add_rule(world.get_location("New Muldul: Vault Front Right Medallion", player), + add_rule(world.get_location("New Muldul: Vault Front Right Medallion", player), lambda state: state._hylics2_enter_foglast(player) and state._hylics2_has_bridge_key(player)) - add_rule(world.get_location("Viewax's Edifice: Fort Wall Medallion", player), + add_rule(world.get_location("Viewax's Edifice: Fort Wall Medallion", player), lambda state: state._hylics2_has_paddle(player)) - add_rule(world.get_location("Viewax's Edifice: Jar Medallion", player), + add_rule(world.get_location("Viewax's Edifice: Jar Medallion", player), lambda state: state._hylics2_has_paddle(player)) - add_rule(world.get_location("Viewax's Edifice: Sage Chair Medallion", player), + add_rule(world.get_location("Viewax's Edifice: Sage Chair Medallion", player), lambda state: state._hylics2_can_air_dash(player)) - add_rule(world.get_location("Arcade 1: Lonely Medallion", player), + add_rule(world.get_location("Arcade 1: Lonely Medallion", player), lambda state: state._hylics2_has_paddle(player)) - add_rule(world.get_location("Arcade 1: Alcove Medallion", player), + add_rule(world.get_location("Arcade 1: Alcove Medallion", player), lambda state: state._hylics2_has_paddle(player)) - add_rule(world.get_location("Foglast: Under Lair Medallion", player), + add_rule(world.get_location("Foglast: Under Lair Medallion", player), lambda state: state._hylics2_has_bridge_key(player)) - add_rule(world.get_location("Foglast: Mid-Air Medallion", player), + add_rule(world.get_location("Foglast: Mid-Air Medallion", player), lambda state: state._hylics2_can_air_dash(player)) - add_rule(world.get_location("Foglast: Top of Tower Medallion", player), + add_rule(world.get_location("Foglast: Top of Tower Medallion", player), lambda state: state._hylics2_has_paddle(player)) - add_rule(world.get_location("Hylemxylem: Lower Reservoir Hole Medallion", player), + add_rule(world.get_location("Hylemxylem: Lower Reservoir Hole Medallion", player), lambda state: state._hylics2_has_upper_chamber_key(player)) # extra rules is Shuffle Red Medallions and Party Shuffle are enabled if world.party_shuffle[player] and world.medallion_shuffle[player]: - add_rule(world.get_location("New Muldul: Vault Rear Left Medallion", player), - lambda state: state._hylics2_has_jail_key(player)) - add_rule(world.get_location("New Muldul: Vault Rear Right Medallion", player), - lambda state: state._hylics2_has_jail_key(player)) - add_rule(world.get_location("New Muldul: Vault Center Medallion", player), - lambda state: state._hylics2_has_jail_key(player)) - add_rule(world.get_location("New Muldul: Vault Front Left Medallion", player), - lambda state: state._hylics2_has_jail_key(player)) - add_rule(world.get_location("New Muldul: Vault Front Right Medallion", player), - lambda state: state._hylics2_has_jail_key(player)) + add_rule(world.get_location("New Muldul: Vault Rear Left Medallion", player), + lambda state: state._hylics2_has_3_members(player)) + add_rule(world.get_location("New Muldul: Vault Rear Right Medallion", player), + lambda state: state._hylics2_has_3_members(player)) + add_rule(world.get_location("New Muldul: Vault Center Medallion", player), + lambda state: state._hylics2_has_3_members(player)) + add_rule(world.get_location("New Muldul: Vault Front Left Medallion", player), + lambda state: state._hylics2_has_3_members(player)) + add_rule(world.get_location("New Muldul: Vault Front Right Medallion", player), + lambda state: state._hylics2_has_3_members(player)) # entrances for i in world.get_region("Airship", player).entrances: @@ -376,7 +376,7 @@ def set_rules(hylics2world): add_rule(i, lambda state: state._hylics2_has_airship(player)) for i in world.get_region("Juice Ranch", player).entrances: add_rule(i, lambda state: state._hylics2_has_airship(player)) - + # random start logic (Viewax's Edifice) elif (world.random_start[player] and hylics2world.start_location == "Viewax's Edifice"): for i in world.get_region("Waynehouse", player).entrances: @@ -430,4 +430,4 @@ def set_rules(hylics2world): for i in world.get_region("TV Island", player).entrances: add_rule(i, lambda state: state._hylics2_has_airship(player)) for i in world.get_region("Sage Labyrinth", player).entrances: - add_rule(i, lambda state: state._hylics2_has_airship(player)) \ No newline at end of file + add_rule(i, lambda state: state._hylics2_has_airship(player)) From bd574ef2619443a771be3ff043b6b19c683df785 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Mon, 5 Dec 2022 03:39:07 +0100 Subject: [PATCH 10/35] WebHost: save datatables state (#1145) * WebHost: save datatables state * WebHost: Fix DataTables local storage keys. Co-authored-by: recklesscoder <57289227+recklesscoder@users.noreply.github.com> --- WebHostLib/static/assets/autodatatable.js | 1 + WebHostLib/static/assets/tracker.js | 7 +++++++ WebHostLib/static/assets/userContent.js | 2 ++ WebHostLib/templates/checkResult.html | 2 +- WebHostLib/templates/genericTracker.html | 4 ++-- WebHostLib/templates/tracker.html | 6 +++--- 6 files changed, 16 insertions(+), 6 deletions(-) diff --git a/WebHostLib/static/assets/autodatatable.js b/WebHostLib/static/assets/autodatatable.js index a1437514e969..d1cf9a6feb8c 100644 --- a/WebHostLib/static/assets/autodatatable.js +++ b/WebHostLib/static/assets/autodatatable.js @@ -4,6 +4,7 @@ window.addEventListener('load', () => { "ordering": true, "info": false, "dom": "t", + "stateSave": true, }); console.log(tables); }); diff --git a/WebHostLib/static/assets/tracker.js b/WebHostLib/static/assets/tracker.js index c4b59958023a..0eeda35a96f4 100644 --- a/WebHostLib/static/assets/tracker.js +++ b/WebHostLib/static/assets/tracker.js @@ -17,6 +17,13 @@ window.addEventListener('load', () => { paging: false, info: false, dom: "t", + stateSave: true, + stateSaveCallback: function(settings,data) { + localStorage.setItem(`DataTables_${settings.sInstance}_/tracker`, JSON.stringify(data)); + }, + stateLoadCallback: function(settings) { + return JSON.parse(localStorage.getItem(`DataTables_${settings.sInstance}_/tracker`)); + }, columnDefs: [ { targets: 'hours', diff --git a/WebHostLib/static/assets/userContent.js b/WebHostLib/static/assets/userContent.js index dcc8f56505bb..18061733af3c 100644 --- a/WebHostLib/static/assets/userContent.js +++ b/WebHostLib/static/assets/userContent.js @@ -6,6 +6,7 @@ window.addEventListener('load', () => { "order": [[ 3, "desc" ]], "info": false, "dom": "t", + "stateSave": true, }); $("#seeds-table").DataTable({ "paging": false, @@ -13,5 +14,6 @@ window.addEventListener('load', () => { "order": [[ 2, "desc" ]], "info": false, "dom": "t", + "stateSave": true, }); }); diff --git a/WebHostLib/templates/checkResult.html b/WebHostLib/templates/checkResult.html index 45a21ae7ac22..c245d7381a4c 100644 --- a/WebHostLib/templates/checkResult.html +++ b/WebHostLib/templates/checkResult.html @@ -12,7 +12,7 @@

Verification Results

The results of your requested file check are below.

- +
diff --git a/WebHostLib/templates/genericTracker.html b/WebHostLib/templates/genericTracker.html index 2a48c66f7ef4..fa070ea6d8d6 100644 --- a/WebHostLib/templates/genericTracker.html +++ b/WebHostLib/templates/genericTracker.html @@ -15,7 +15,7 @@ This tracker will automatically update itself periodically.
-
File
+
@@ -37,7 +37,7 @@
Item
- +
diff --git a/WebHostLib/templates/tracker.html b/WebHostLib/templates/tracker.html index 889ed2b2723e..4b97d67e5644 100644 --- a/WebHostLib/templates/tracker.html +++ b/WebHostLib/templates/tracker.html @@ -25,7 +25,7 @@
{% for team, players in inventory.items() %}
-
Location
+
@@ -78,7 +78,7 @@ {% for team, players in checks_done.items() %}
-
#
+
@@ -153,7 +153,7 @@ {% endfor %} {% for team, hints in hints.items() %}
-
#
+
From 9bdbced51fc153b8d3a8401149b53f668de5358c Mon Sep 17 00:00:00 2001 From: espeon65536 <81029175+espeon65536@users.noreply.github.com> Date: Sun, 4 Dec 2022 21:04:01 -0600 Subject: [PATCH 11/35] Hylics 2: create victory location earlier to ensure it is cached correctly (#1291) Fixes generation issues where the victory location could not be found from MultiWorld.get_locations --- worlds/hylics2/__init__.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/worlds/hylics2/__init__.py b/worlds/hylics2/__init__.py index 80c80a0d3696..26a1a1131b35 100644 --- a/worlds/hylics2/__init__.py +++ b/worlds/hylics2/__init__.py @@ -76,14 +76,6 @@ def generate_early(self): self.start_location = "Shield Facility" def generate_basic(self): - # create location for beating the game and place Victory event there - loc = Location(self.player, "Defeat Gibby", None, self.multiworld.get_region("Hylemxylem", self.player)) - loc.place_locked_item(self.create_event("Victory")) - set_rule(loc, lambda state: state._hylics2_has_upper_chamber_key(self.player) - and state._hylics2_has_vessel_room_key(self.player)) - self.multiworld.get_region("Hylemxylem", self.player).locations.append(loc) - self.multiworld.completion_condition[self.player] = lambda state: state.has("Victory", self.player) - # create item pool pool = [] @@ -237,6 +229,14 @@ def create_regions(self) -> None: region_table[data["region"]].locations\ .append(Hylics2Location(self.player, data["name"], i, region_table[data["region"]])) + # create location for beating the game and place Victory event there + loc = Location(self.player, "Defeat Gibby", None, self.multiworld.get_region("Hylemxylem", self.player)) + loc.place_locked_item(self.create_event("Victory")) + set_rule(loc, lambda state: state._hylics2_has_upper_chamber_key(self.player) + and state._hylics2_has_vessel_room_key(self.player)) + self.multiworld.get_region("Hylemxylem", self.player).locations.append(loc) + self.multiworld.completion_condition[self.player] = lambda state: state.has("Victory", self.player) + class Hylics2Location(Location): game: str = "Hylics 2" From 44124349764d6a2cb35899e097f526b78bac478e Mon Sep 17 00:00:00 2001 From: Yoshi348 <71361685+Yoshi348@users.noreply.github.com> Date: Mon, 5 Dec 2022 13:26:44 -0800 Subject: [PATCH 12/35] meta.yaml: update progression balancing (#1283) * Update meta.yaml to numeric progression balancing Instead of the old on/off system * normal/disabled --- meta.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/meta.yaml b/meta.yaml index fabb57efd0fd..f47a7a31f7e5 100644 --- a/meta.yaml +++ b/meta.yaml @@ -7,9 +7,9 @@ meta_description: Meta-Mystery file with the intention of having similar-length completion times for a hopefully better experience null: progression_balancing: # Progression balancing tries to make sure that the player has *something* towards any players goal in each "sphere" - on: 0 # Force every player into progression balancing - off: 0 # Force every player out of progression balancing, then prepare for a lot of logical BK - null: 1 # Let players decide via their own progression_balancing flag in their yaml, defaulting to on + normal: 0 # Force every player into default progression balancing + disabled: 0 # Force every player out of progression balancing, then prepare for a lot of logical BK + null: 1 # Let players decide via their own progression_balancing setting in their yaml, defaulting to 50 A Link to the Past: goals: ganon: 100 # Climb GT, defeat Agahnim 2, and then kill Ganon @@ -36,4 +36,4 @@ A Link to the Past: 30: 50 triforce_pieces_required: # Set to how many out of X triforce pieces you need to win the game in a triforce hunt. Default is 20. Max is 90, Min is 1 # Format "pieces: chance" - 25: 50 \ No newline at end of file + 25: 50 From 32b8f9f9f389a3f14c9fa1a8bea6453de03b5a0f Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Mon, 5 Dec 2022 22:27:15 +0100 Subject: [PATCH 13/35] WebHost: restore old silent ignore of mimetypes in json getting (#1292) * WebHost: restore old silent ignore of mimetypes in json getting of /api/generate * Tests: add tests for /api/generate --- WebHostLib/api/generate.py | 5 ++++- test/webhost/TestAPIGenerate.py | 40 +++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 test/webhost/TestAPIGenerate.py diff --git a/WebHostLib/api/generate.py b/WebHostLib/api/generate.py index 0f090039fb44..528a14df0570 100644 --- a/WebHostLib/api/generate.py +++ b/WebHostLib/api/generate.py @@ -27,7 +27,10 @@ def generate_api(): race = bool(0 if request.form["race"] in {"false"} else int(request.form["race"])) meta_options_source = request.form - json_data = request.get_json() + # json_data is optional, we can have it silently fall to None as it used to do. + # See https://flask.palletsprojects.com/en/2.2.x/api/#flask.Request.get_json -> Changelog -> 2.1 + json_data = request.get_json(silent=True) + if json_data: meta_options_source = json_data if 'weights' in json_data: diff --git a/test/webhost/TestAPIGenerate.py b/test/webhost/TestAPIGenerate.py new file mode 100644 index 000000000000..e50bf96e19ab --- /dev/null +++ b/test/webhost/TestAPIGenerate.py @@ -0,0 +1,40 @@ +import unittest +import json + + +class TestDocs(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + from WebHost import get_app, raw_app + raw_app.config["PONY"] = { + "provider": "sqlite", + "filename": ":memory:", + "create_db": True, + } + app = get_app() + app.config.update({ + "TESTING": True, + }) + cls.client = app.test_client() + + def testCorrectErrorEmptyRequest(self): + response = self.client.post("/api/generate") + self.assertIn("No options found. Expected file attachment or json weights.", response.text) + + def testGenerationQueued(self): + options = { + "Tester1": + { + "game": "Archipelago", + "name": "Tester", + "Archipelago": {} + } + } + response = self.client.post( + "/api/generate", + data=json.dumps({"weights": options}), + content_type='application/json' + ) + json_data = response.get_json() + self.assertTrue(json_data["text"].startswith("Generation of seed ")) + self.assertTrue(json_data["text"].endswith(" started successfully.")) From ffc000ec911d9e004902983303220afc7de0f2c3 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sat, 3 Dec 2022 04:22:58 +0100 Subject: [PATCH 14/35] Network: remove deprecated IgnoreGame tag --- CommonClient.py | 2 +- MultiServer.py | 3 +-- docs/network protocol.md | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/CommonClient.py b/CommonClient.py index 92761542b93a..aa11661d5bb3 100644 --- a/CommonClient.py +++ b/CommonClient.py @@ -799,7 +799,7 @@ def get_base_parser(description: typing.Optional[str] = None): # Text Mode to use !hint and such with games that have no text entry class TextContext(CommonContext): - tags = {"AP", "IgnoreGame", "TextOnly"} + tags = {"AP", "TextOnly"} game = "" # empty matches any game since 0.3.2 items_handling = 0b111 # receive all items for /received want_slot_data = False # Can't use game specific slot_data diff --git a/MultiServer.py b/MultiServer.py index ad134901b980..072e5aa1e69a 100644 --- a/MultiServer.py +++ b/MultiServer.py @@ -1541,8 +1541,7 @@ async def process_client_cmd(ctx: Context, client: Client, args: dict): else: team, slot = ctx.connect_names[args['name']] game = ctx.games[slot] - ignore_game = "IgnoreGame" in args["tags"] or ( # IgnoreGame is deprecated. TODO: remove after 0.3.3? - ("TextOnly" in args["tags"] or "Tracker" in args["tags"]) and not args.get("game")) + ignore_game = ("TextOnly" in args["tags"] or "Tracker" in args["tags"]) and not args.get("game") if not ignore_game and args['game'] != game: errors.add('InvalidGame') minver = min_client_version if ignore_game else ctx.minimum_client_versions[slot] diff --git a/docs/network protocol.md b/docs/network protocol.md index c870d2f6edbe..74e3fab102b2 100644 --- a/docs/network protocol.md +++ b/docs/network protocol.md @@ -644,7 +644,6 @@ Tags are represented as a list of strings, the common Client tags follow: | Name | Notes | |------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | AP | Signifies that this client is a reference client, its usefulness is mostly in debugging to compare client behaviours more easily. | -| IgnoreGame | Deprecated. See Tracker and TextOnly. Tells the server to ignore the "game" attribute in the [Connect](#Connect) packet. | | DeathLink | Client participates in the DeathLink mechanic, therefore will send and receive DeathLink bounce packets | | Tracker | Tells the server that this client will not send locations and is actually a Tracker. When specified and used with empty or null `game` in [Connect](#connect), game and game's version validation will be skipped. | | TextOnly | Tells the server that this client will not send locations and is intended for chat. When specified and used with empty or null `game` in [Connect](#connect), game and game's version validation will be skipped. | From 0e4fa378dd7549e2a9bcca7be6b61f2ea333c48d Mon Sep 17 00:00:00 2001 From: recklesscoder <57289227+recklesscoder@users.noreply.github.com> Date: Tue, 6 Dec 2022 00:40:51 +0100 Subject: [PATCH 15/35] WebHost: Detect confusion of settings zip and seed zip (#1227) --- WebHostLib/api/generate.py | 6 ++++-- WebHostLib/check.py | 10 +++++++--- WebHostLib/generate.py | 2 +- WebHostLib/static/styles/globalStyles.css | 3 +++ WebHostLib/upload.py | 6 +++++- 5 files changed, 20 insertions(+), 7 deletions(-) diff --git a/WebHostLib/api/generate.py b/WebHostLib/api/generate.py index 528a14df0570..1d9e6fd9c192 100644 --- a/WebHostLib/api/generate.py +++ b/WebHostLib/api/generate.py @@ -2,7 +2,7 @@ import pickle from uuid import UUID -from flask import request, session, url_for +from flask import request, session, url_for, Markup from pony.orm import commit from WebHostLib import app @@ -21,7 +21,9 @@ def generate_api(): if 'file' in request.files: file = request.files['file'] options = get_yaml_data(file) - if type(options) == str: + if isinstance(options, Markup): + return {"text": options.striptags()}, 400 + if isinstance(options, str): return {"text": options}, 400 if "race" in request.form: race = bool(0 if request.form["race"] in {"false"} else int(request.form["race"])) diff --git a/WebHostLib/check.py b/WebHostLib/check.py index cd45dff44065..5ca44231270d 100644 --- a/WebHostLib/check.py +++ b/WebHostLib/check.py @@ -1,7 +1,7 @@ import zipfile from typing import * -from flask import request, flash, redirect, url_for, render_template +from flask import request, flash, redirect, url_for, render_template, Markup from WebHostLib import app @@ -25,7 +25,7 @@ def check(): else: file = request.files['file'] options = get_yaml_data(file) - if type(options) == str: + if isinstance(options, str): flash(options) else: results, _ = roll_options(options) @@ -38,7 +38,7 @@ def mysterycheck(): return redirect(url_for("check"), 301) -def get_yaml_data(file) -> Union[Dict[str, str], str]: +def get_yaml_data(file) -> Union[Dict[str, str], str, Markup]: options = {} # if user does not select file, browser also # submit an empty part without filename @@ -50,6 +50,10 @@ def get_yaml_data(file) -> Union[Dict[str, str], str]: with zipfile.ZipFile(file, 'r') as zfile: infolist = zfile.infolist() + if any(file.filename.endswith(".archipelago") for file in infolist): + return Markup("Error: Your .zip file contains an .archipelago file. " + 'Did you mean to host a game?') + for file in infolist: if file.filename.endswith(banned_zip_contents): return "Uploaded data contained a rom file, which is likely to contain copyrighted material. Your file was deleted." diff --git a/WebHostLib/generate.py b/WebHostLib/generate.py index 11d70da2fbb5..c229698c4957 100644 --- a/WebHostLib/generate.py +++ b/WebHostLib/generate.py @@ -52,7 +52,7 @@ def generate(race=False): else: file = request.files['file'] options = get_yaml_data(file) - if type(options) == str: + if isinstance(options, str): flash(options) else: meta = get_meta(request.form) diff --git a/WebHostLib/static/styles/globalStyles.css b/WebHostLib/static/styles/globalStyles.css index d8b10d1c50c7..a787b0c6570a 100644 --- a/WebHostLib/static/styles/globalStyles.css +++ b/WebHostLib/static/styles/globalStyles.css @@ -105,6 +105,9 @@ h5, h6{ margin-bottom: 20px; background-color: #ffff00; } +.user-message a{ + color: #ff7700; +} .interactive{ color: #ffef00; diff --git a/WebHostLib/upload.py b/WebHostLib/upload.py index 1aa60ffc5f99..dd0d218ed2ce 100644 --- a/WebHostLib/upload.py +++ b/WebHostLib/upload.py @@ -5,7 +5,7 @@ import zipfile from io import BytesIO -from flask import request, flash, redirect, url_for, session, render_template +from flask import request, flash, redirect, url_for, session, render_template, Markup from pony.orm import flush, select import MultiServer @@ -22,6 +22,10 @@ def upload_zip_to_db(zfile: zipfile.ZipFile, owner=None, meta={"race": False}, s if not owner: owner = session["_id"] infolist = zfile.infolist() + if all(file.filename.endswith((".yaml", ".yml")) or file.is_dir() for file in infolist): + flash(Markup("Error: Your .zip file only contains .yaml files. " + 'Did you mean to generate a game?')) + return slots: typing.Set[Slot] = set() spoiler = "" files = {} From 2cc03d003a366d83d99f1b0acc355f175655c187 Mon Sep 17 00:00:00 2001 From: alwaysintreble Date: Mon, 5 Dec 2022 17:50:11 -0600 Subject: [PATCH 16/35] Core: fix bug that caused world option overrides to fail (#1293) * core: fix bug that caused world option overrides to fail * copy paste sliver's better code that works as intended * Fix whitespace Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com> --- Generate.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Generate.py b/Generate.py index ccf054842b30..04c081b4ac46 100644 --- a/Generate.py +++ b/Generate.py @@ -503,7 +503,8 @@ def roll_settings(weights: dict, plando_options: PlandoSettings = PlandoSettings handle_option(ret, game_weights, option_key, option, plando_options) for option_key, option in Options.per_game_common_options.items(): # skip setting this option if already set from common_options, defaulting to root option - if not (option_key in Options.common_options and option_key not in game_weights): + if option_key not in world_type.option_definitions and \ + (option_key not in Options.common_options or option_key in game_weights): handle_option(ret, game_weights, option_key, option, plando_options) if PlandoSettings.items in plando_options: ret.plando_items = game_weights.get("plando_items", []) From 82444229bebeca3b0718a64386104668fe8a411b Mon Sep 17 00:00:00 2001 From: Zach Parks Date: Tue, 6 Dec 2022 20:49:55 -0600 Subject: [PATCH 17/35] Rogue Legacy: More refactoring and clean up. (#1297) * Rogue Legacy: More refactoring and clean up. * Also marked Blacksmith as Progression as it's used in a rule. * Remove extra newline. * Prevent divide by zero type error. * Scratch last commit, got the math mixed in my head. * Clarified name of rule regarding percentage of stat upgrades. * Move early vendors/architect creation into `create_items` logic. * Rename parameter in `create_region`. * Rename local var in `create_region`. * Removed accidental links in Markdown docs. * Refactor `create_region` signature and caller. * Remove redundant if-else. * Revert change to if-else, and moved item_pool to function instead of obj var. * Rename LegacyLogic to RLLogic. * Remove LogicMixin for rules. --- worlds/rogue_legacy/Items.py | 6 +- worlds/rogue_legacy/Locations.py | 4 - worlds/rogue_legacy/Regions.py | 60 ++++------ worlds/rogue_legacy/Rules.py | 85 ++++++++----- worlds/rogue_legacy/Traits.py | 38 ------ worlds/rogue_legacy/__init__.py | 126 ++++++++++---------- worlds/rogue_legacy/docs/en_Rogue Legacy.md | 6 +- worlds/rogue_legacy/docs/rogue-legacy_en.md | 16 +-- 8 files changed, 153 insertions(+), 188 deletions(-) delete mode 100644 worlds/rogue_legacy/Traits.py diff --git a/worlds/rogue_legacy/Items.py b/worlds/rogue_legacy/Items.py index b52d603a1dd4..efa24df05ac2 100644 --- a/worlds/rogue_legacy/Items.py +++ b/worlds/rogue_legacy/Items.py @@ -14,10 +14,6 @@ class RLItemData(NamedTuple): max_quantity: int = 1 weight: int = 1 - @property - def is_event_item(self): - return self.code is None - def get_items_by_category(category: str) -> Dict[str, RLItemData]: item_dict: Dict[str, RLItemData] = {} @@ -30,7 +26,7 @@ def get_items_by_category(category: str) -> Dict[str, RLItemData]: item_table: Dict[str, RLItemData] = { # Vendors - "Blacksmith": RLItemData("Vendors", 90_000, ItemClassification.useful), + "Blacksmith": RLItemData("Vendors", 90_000, ItemClassification.progression), "Enchantress": RLItemData("Vendors", 90_001, ItemClassification.progression), "Architect": RLItemData("Vendors", 90_002, ItemClassification.useful), diff --git a/worlds/rogue_legacy/Locations.py b/worlds/rogue_legacy/Locations.py index 2d4197e6db18..db9e1db3b09a 100644 --- a/worlds/rogue_legacy/Locations.py +++ b/worlds/rogue_legacy/Locations.py @@ -11,10 +11,6 @@ class RLLocationData(NamedTuple): category: str code: Optional[int] = None - @property - def is_event_location(self): - return self.code is None - def get_locations_by_category(category: str) -> Dict[str, RLLocationData]: location_dict: Dict[str, RLLocationData] = {} diff --git a/worlds/rogue_legacy/Regions.py b/worlds/rogue_legacy/Regions.py index e2b05949577c..025f13f41923 100644 --- a/worlds/rogue_legacy/Regions.py +++ b/worlds/rogue_legacy/Regions.py @@ -1,31 +1,27 @@ from typing import Dict, List, NamedTuple, Optional from BaseClasses import MultiWorld, Region, RegionType, Entrance -from .Items import RLItem from .Locations import RLLocation, location_table, get_locations_by_category class RLRegionData(NamedTuple): locations: Optional[List[str]] - exits: Optional[List[str]] + region_exits: Optional[List[str]] def create_regions(multiworld: MultiWorld, player: int): regions: Dict[str, RLRegionData] = { - "Menu": RLRegionData(None, ["Castle Hamson"]), - "The Manor": RLRegionData([], []), - "Castle Hamson": RLRegionData([], ["Forest Abkhazia", - "The Maya", - "Land of Darkness", - "The Fountain Room", - "The Manor"]), - "Forest Abkhazia": RLRegionData([], []), - "The Maya": RLRegionData([], []), - "Land of Darkness": RLRegionData([], []), - "The Fountain Room": RLRegionData([], None), + "Menu": RLRegionData(None, ["Castle Hamson"]), + "The Manor": RLRegionData([], []), + "Castle Hamson": RLRegionData([], ["Forest Abkhazia", "The Maya", "Land of Darkness", + "The Fountain Room", "The Manor"]), + "Forest Abkhazia": RLRegionData([], []), + "The Maya": RLRegionData([], []), + "Land of Darkness": RLRegionData([], []), + "The Fountain Room": RLRegionData([], None), } - # Diaries + # Artificially stagger diary spheres for progression. for diary in range(0, 25): region: str if 0 <= diary < 6: @@ -38,7 +34,6 @@ def create_regions(multiworld: MultiWorld, player: int): region = "Land of Darkness" else: region = "The Fountain Room" - regions[region].locations.append(f"Diary {diary + 1}") # Manor & Special @@ -90,7 +85,7 @@ def create_regions(multiworld: MultiWorld, player: int): # Set up the regions correctly. for name, data in regions.items(): - multiworld.regions.append(create_region(multiworld, player, name, data.locations, data.exits)) + multiworld.regions.append(create_region(multiworld, player, name, data)) multiworld.get_entrance("Castle Hamson", player).connect(multiworld.get_region("Castle Hamson", player)) multiworld.get_entrance("The Manor", player).connect(multiworld.get_region("The Manor", player)) @@ -100,24 +95,17 @@ def create_regions(multiworld: MultiWorld, player: int): multiworld.get_entrance("The Fountain Room", player).connect(multiworld.get_region("The Fountain Room", player)) -def create_region(multiworld: MultiWorld, player: int, name: str, locations=None, exits=None): - ret = Region(name, RegionType.Generic, name, player) - ret.multiworld = multiworld - if locations: - for loc_name in locations: +def create_region(multiworld: MultiWorld, player: int, name: str, data: RLRegionData): + region = Region(name, RegionType.Generic, name, player, multiworld) + if data.locations: + for loc_name in data.locations: loc_data = location_table.get(loc_name) - location = RLLocation(player, loc_name, loc_data.code if loc_data else None, ret) - - # Special rule handling for fairy chests. - if "Fairy" in loc_name: - location.access_rule = lambda state: state.has("Dragons", player) or ( - state.has("Enchantress", player) and ( - state.has("Vault Runes", player) or - state.has("Sprint Runes", player) or - state.has("Sky Runes", player))) - ret.locations.append(location) - if exits: - for exit in exits: - entrance = Entrance(player, exit, ret) - ret.exits.append(entrance) - return ret + location = RLLocation(player, loc_name, loc_data.code if loc_data else None, region) + region.locations.append(location) + + if data.region_exits: + for exit in data.region_exits: + entrance = Entrance(player, exit, region) + region.exits.append(entrance) + + return region diff --git a/worlds/rogue_legacy/Rules.py b/worlds/rogue_legacy/Rules.py index 18a4a160e057..92b9ba0a6cce 100644 --- a/worlds/rogue_legacy/Rules.py +++ b/worlds/rogue_legacy/Rules.py @@ -1,37 +1,61 @@ from BaseClasses import MultiWorld, CollectionState -from ..AutoWorld import LogicMixin from ..generic.Rules import set_rule -class LegacyLogic(LogicMixin): - def has_any_vendors(self: CollectionState, player: int) -> bool: - return self.has_any({"Blacksmith", "Enchantress"}, player) +def get_upgrade_total(multiworld: MultiWorld, player: int) -> int: + return int(multiworld.health_pool[player]) + int(multiworld.mana_pool[player]) + \ + int(multiworld.attack_pool[player]) + int(multiworld.magic_damage_pool[player]) - def has_all_vendors(self: CollectionState, player: int) -> bool: - return self.has_all({"Blacksmith", "Enchantress"}, player) - def has_stat_upgrades(self, player: int, amount: int) -> bool: - return self.stat_upgrade_count(player) >= amount +def get_upgrade_count(state: CollectionState, player: int) -> int: + return state.item_count("Health Up", player) + state.item_count("Mana Up", player) + \ + state.item_count("Attack Up", player) + state.item_count("Magic Damage Up", player) - def total_stat_upgrades_count(self, player: int) -> int: - return int(self.multiworld.health_pool[player]) + \ - int(self.multiworld.mana_pool[player]) + \ - int(self.multiworld.attack_pool[player]) + \ - int(self.multiworld.magic_damage_pool[player]) - def stat_upgrade_count(self: CollectionState, player: int) -> int: - return self.item_count("Health Up", player) + self.item_count("Mana Up", player) + \ - self.item_count("Attack Up", player) + self.item_count("Magic Damage Up", player) +def has_vendors(state: CollectionState, player: int) -> bool: + return state.has_all({"Blacksmith", "Enchantress"}, player) + + +def has_upgrade_amount(state: CollectionState, player: int, amount: int) -> bool: + return get_upgrade_count(state, player) >= amount + + +def has_upgrades_percentage(state: CollectionState, player: int, percentage: float) -> bool: + return has_upgrade_amount(state, player, get_upgrade_total(state.multiworld, player) * (round(percentage) // 100)) + + +def has_movement_rune(state: CollectionState, player: int) -> bool: + return state.has("Vault Runes", player) or state.has("Sprint Runes", player) or state.has("Sky Runes", player) + + +def has_fairy_progression(state: CollectionState, player: int) -> bool: + return state.has("Dragons", player) or (state.has("Enchantress", player) and has_movement_rune(state, player)) + + +def has_defeated_castle(state: CollectionState, player: int) -> bool: + return state.has("Defeat Khidr", player) or state.has("Defeat Neo Khidr", player) + + +def has_defeated_forest(state: CollectionState, player: int) -> bool: + return state.has("Defeat Alexander", player) or state.has("Defeat Alexander IV", player) + + +def has_defeated_tower(state: CollectionState, player: int) -> bool: + return state.has("Defeat Ponce de Leon", player) or state.has("Defeat Ponce de Freon", player) + + +def has_defeated_dungeon(state: CollectionState, player: int) -> bool: + return state.has("Defeat Herodotus", player) or state.has("Defeat Astrodotus", player) def set_rules(multiworld: MultiWorld, player: int): - # Vendors + # If 'vendors' are 'normal', then expect it to show up in the first half(ish) of the spheres. if multiworld.vendors[player] == "normal": set_rule(multiworld.get_location("Forest Abkhazia Boss Reward", player), - lambda state: state.has_all_vendors(player)) + lambda state: has_vendors(state, player)) - # Scale each manor location. + # Gate each manor location so everything isn't dumped into sphere 1. manor_rules = { "Defeat Khidr" if multiworld.khidr[player] == "vanilla" else "Defeat Neo Khidr": [ "Manor - Left Wing Window", @@ -65,22 +89,27 @@ def set_rules(multiworld: MultiWorld, player: int): ] } + # Set rules for manor locations. for event, locations in manor_rules.items(): for location in locations: set_rule(multiworld.get_location(location, player), lambda state: state.has(event, player)) - # Standard Zone Progression + # Set rules for fairy chests to decrease headache of expectation to find non-movement fairy chests. + for fairy_location in [location for location in multiworld.get_locations(player) if "Fairy" in location.name]: + set_rule(fairy_location, lambda state: has_fairy_progression(state, player)) + + # Region rules. multiworld.get_entrance("Forest Abkhazia", player).access_rule = \ - (lambda state: state.has_stat_upgrades(player, 0.125 * state.total_stat_upgrades_count(player)) and - (state.has("Defeat Khidr", player) or state.has("Defeat Neo Khidr", player))) + lambda state: has_upgrades_percentage(state, player, 12.5) and has_defeated_castle(state, player) + multiworld.get_entrance("The Maya", player).access_rule = \ - (lambda state: state.has_stat_upgrades(player, 0.25 * state.total_stat_upgrades_count(player)) and - (state.has("Defeat Alexander", player) or state.has("Defeat Alexander IV", player))) + lambda state: has_upgrades_percentage(state, player, 25) and has_defeated_forest(state, player) + multiworld.get_entrance("Land of Darkness", player).access_rule = \ - (lambda state: state.has_stat_upgrades(player, 0.375 * state.total_stat_upgrades_count(player)) and - (state.has("Defeat Ponce de Leon", player) or state.has("Defeat Ponce de Freon", player))) + lambda state: has_upgrades_percentage(state, player, 37.5) and has_defeated_tower(state, player) + multiworld.get_entrance("The Fountain Room", player).access_rule = \ - (lambda state: state.has_stat_upgrades(player, 0.5 * state.total_stat_upgrades_count(player)) and - (state.has("Defeat Herodotus", player) or state.has("Defeat Astrodotus", player))) + lambda state: has_upgrades_percentage(state, player, 50) and has_defeated_dungeon(state, player) + # Win condition. multiworld.completion_condition[player] = lambda state: state.has("Defeat The Fountain", player) diff --git a/worlds/rogue_legacy/Traits.py b/worlds/rogue_legacy/Traits.py deleted file mode 100644 index 19a679756aaa..000000000000 --- a/worlds/rogue_legacy/Traits.py +++ /dev/null @@ -1,38 +0,0 @@ -traits = [ - "Color Blind", - "Gay", - "Near-Sighted", - "Far-Sighted", - "Dyslexia", - "Gigantism", - "Dwarfism", - "Baldness", - "Endomorph", - "Ectomorph", - "Alzheimers", - "Dextrocardia", - "Coprolalia", - "ADHD", - "O.C.D.", - "Hypergonadism", - "Muscle Wk.", - "Stereo Blind", - "I.B.S.", - "Vertigo", - "Tunnel Vision", - "Ambilevous", - "P.A.D.", - "Alektorophobia", - "Hypochondriac", - "Dementia", - "Flexible", - "Eid. Mem.", - "Nostalgic", - "C.I.P.", - "Savant", - "The One", - "Clumsy", - "EHS", - "Glaucoma", - "Adopted", -] diff --git a/worlds/rogue_legacy/__init__.py b/worlds/rogue_legacy/__init__.py index 93334a9770cb..34f7c785ba5f 100644 --- a/worlds/rogue_legacy/__init__.py +++ b/worlds/rogue_legacy/__init__.py @@ -30,7 +30,7 @@ class RLWorld(World): you. Every child is unique. One child might be colorblind, another might have vertigo-- they could even be a dwarf. But that's OK, because no one is perfect, and you don't have to be to succeed. """ - game: str = "Rogue Legacy" + game = "Rogue Legacy" option_definitions = rl_options topology_present = True data_version = 4 @@ -40,158 +40,154 @@ class RLWorld(World): item_name_to_id = {name: data.code for name, data in item_table.items()} location_name_to_id = {name: data.code for name, data in location_table.items()} - item_pool: List[RLItem] = [] - - def setting(self, name: str): + # TODO: Replace calls to this function with "options-dict", once that PR is completed and merged. + def get_setting(self, name: str): return getattr(self.multiworld, name)[self.player] def fill_slot_data(self) -> dict: - return {option_name: self.setting(option_name).value for option_name in rl_options} + return {option_name: self.get_setting(option_name).value for option_name in rl_options} def generate_early(self): # Check validation of names. - additional_lady_names = len(self.setting("additional_lady_names").value) - additional_sir_names = len(self.setting("additional_sir_names").value) - if not self.setting("allow_default_names"): - # Check for max_quantity. - if additional_lady_names < int(self.setting("number_of_children")): + additional_lady_names = len(self.get_setting("additional_lady_names").value) + additional_sir_names = len(self.get_setting("additional_sir_names").value) + if not self.get_setting("allow_default_names"): + if additional_lady_names < int(self.get_setting("number_of_children")): raise Exception( f"allow_default_names is off, but not enough names are defined in additional_lady_names. " - f"Expected {int(self.setting('number_of_children'))}, Got {additional_lady_names}") + f"Expected {int(self.get_setting('number_of_children'))}, Got {additional_lady_names}") - if additional_sir_names < int(self.setting("number_of_children")): + if additional_sir_names < int(self.get_setting("number_of_children")): raise Exception( f"allow_default_names is off, but not enough names are defined in additional_sir_names. " - f"Expected {int(self.setting('number_of_children'))}, Got {additional_sir_names}") - - if self.setting("vendors") == "early": - self.multiworld.local_early_items[self.player]["Blacksmith"] = 1 - self.multiworld.local_early_items[self.player]["Enchantress"] = 1 - - if self.setting("architect") == "early": - self.multiworld.local_early_items[self.player]["Architect"] = 1 + f"Expected {int(self.get_setting('number_of_children'))}, Got {additional_sir_names}") - def generate_basic(self): - self.item_pool = [] - total_locations = 64 + (self.setting("chests_per_zone") * 4) + (self.setting("fairy_chests_per_zone") * 4) - - # Add items to item pool. Anything with a "continue" will not be added to the item pool. + def create_items(self): + item_pool: List[RLItem] = [] + total_locations = len(self.multiworld.get_unfilled_locations(self.player)) for name, data in item_table.items(): quantity = data.max_quantity # Architect if name == "Architect": - if self.setting("architect") == "disabled": + if self.get_setting("architect") == "disabled": continue - if self.setting("architect") == "start_unlocked": + if self.get_setting("architect") == "start_unlocked": self.multiworld.push_precollected(self.create_item(name)) continue + if self.get_setting("architect") == "early": + self.multiworld.local_early_items[self.player]["Architect"] = 1 + continue # Blacksmith and Enchantress if name == "Blacksmith" or name == "Enchantress": - if self.setting("vendors") == "start_unlocked": + if self.get_setting("vendors") == "start_unlocked": self.multiworld.push_precollected(self.create_item(name)) continue + if self.get_setting("vendors") == "early": + self.multiworld.local_early_items[self.player]["Blacksmith"] = 1 + self.multiworld.local_early_items[self.player]["Enchantress"] = 1 + continue # Haggling - if name == "Haggling" and self.setting("disable_charon"): + if name == "Haggling" and self.get_setting("disable_charon"): continue # Blueprints if data.category == "Blueprints": # No progressive blueprints if progressive_blueprints are disabled. - if name == "Progressive Blueprints" and not self.setting("progressive_blueprints"): + if name == "Progressive Blueprints" and not self.get_setting("progressive_blueprints"): continue # No distinct blueprints if progressive_blueprints are enabled. - elif name != "Progressive Blueprints" and self.setting("progressive_blueprints"): + elif name != "Progressive Blueprints" and self.get_setting("progressive_blueprints"): continue # Classes if data.category == "Classes": if name == "Progressive Knights": - if "Knight" not in self.setting("available_classes"): + if "Knight" not in self.get_setting("available_classes"): continue - if self.setting("starting_class") == "knight": + if self.get_setting("starting_class") == "knight": quantity = 1 if name == "Progressive Mages": - if "Mage" not in self.setting("available_classes"): + if "Mage" not in self.get_setting("available_classes"): continue - if self.setting("starting_class") == "mage": + if self.get_setting("starting_class") == "mage": quantity = 1 if name == "Progressive Barbarians": - if "Barbarian" not in self.setting("available_classes"): + if "Barbarian" not in self.get_setting("available_classes"): continue - if self.setting("starting_class") == "barbarian": + if self.get_setting("starting_class") == "barbarian": quantity = 1 if name == "Progressive Knaves": - if "Knave" not in self.setting("available_classes"): + if "Knave" not in self.get_setting("available_classes"): continue - if self.setting("starting_class") == "knave": + if self.get_setting("starting_class") == "knave": quantity = 1 if name == "Progressive Miners": - if "Miner" not in self.setting("available_classes"): + if "Miner" not in self.get_setting("available_classes"): continue - if self.setting("starting_class") == "miner": + if self.get_setting("starting_class") == "miner": quantity = 1 if name == "Progressive Shinobis": - if "Shinobi" not in self.setting("available_classes"): + if "Shinobi" not in self.get_setting("available_classes"): continue - if self.setting("starting_class") == "shinobi": + if self.get_setting("starting_class") == "shinobi": quantity = 1 if name == "Progressive Liches": - if "Lich" not in self.setting("available_classes"): + if "Lich" not in self.get_setting("available_classes"): continue - if self.setting("starting_class") == "lich": + if self.get_setting("starting_class") == "lich": quantity = 1 if name == "Progressive Spellthieves": - if "Spellthief" not in self.setting("available_classes"): + if "Spellthief" not in self.get_setting("available_classes"): continue - if self.setting("starting_class") == "spellthief": + if self.get_setting("starting_class") == "spellthief": quantity = 1 if name == "Dragons": - if "Dragon" not in self.setting("available_classes"): + if "Dragon" not in self.get_setting("available_classes"): continue if name == "Traitors": - if "Traitor" not in self.setting("available_classes"): + if "Traitor" not in self.get_setting("available_classes"): continue # Skills if name == "Health Up": - quantity = self.setting("health_pool") + quantity = self.get_setting("health_pool") elif name == "Mana Up": - quantity = self.setting("mana_pool") + quantity = self.get_setting("mana_pool") elif name == "Attack Up": - quantity = self.setting("attack_pool") + quantity = self.get_setting("attack_pool") elif name == "Magic Damage Up": - quantity = self.setting("magic_damage_pool") + quantity = self.get_setting("magic_damage_pool") elif name == "Armor Up": - quantity = self.setting("armor_pool") + quantity = self.get_setting("armor_pool") elif name == "Equip Up": - quantity = self.setting("equip_pool") + quantity = self.get_setting("equip_pool") elif name == "Crit Chance Up": - quantity = self.setting("crit_chance_pool") + quantity = self.get_setting("crit_chance_pool") elif name == "Crit Damage Up": - quantity = self.setting("crit_damage_pool") + quantity = self.get_setting("crit_damage_pool") # Ignore filler, it will be added in a later stage. if data.category == "Filler": continue - self.item_pool += [self.create_item(name) for _ in range(0, quantity)] + item_pool += [self.create_item(name) for _ in range(0, quantity)] # Fill any empty locations with filler items. - while len(self.item_pool) < total_locations: - self.item_pool.append(self.create_item(self.get_filler_item_name())) + while len(item_pool) < total_locations: + item_pool.append(self.create_item(self.get_filler_item_name())) - self.multiworld.itempool += self.item_pool + self.multiworld.itempool += item_pool def get_filler_item_name(self) -> str: fillers = get_items_by_category("Filler") @@ -219,7 +215,7 @@ def _place_events(self): self.create_event("Defeat The Fountain")) # Khidr / Neo Khidr - if self.setting("khidr") == "vanilla": + if self.get_setting("khidr") == "vanilla": self.multiworld.get_location("Castle Hamson Boss Room", self.player).place_locked_item( self.create_event("Defeat Khidr")) else: @@ -227,7 +223,7 @@ def _place_events(self): self.create_event("Defeat Neo Khidr")) # Alexander / Alexander IV - if self.setting("alexander") == "vanilla": + if self.get_setting("alexander") == "vanilla": self.multiworld.get_location("Forest Abkhazia Boss Room", self.player).place_locked_item( self.create_event("Defeat Alexander")) else: @@ -235,7 +231,7 @@ def _place_events(self): self.create_event("Defeat Alexander IV")) # Ponce de Leon / Ponce de Freon - if self.setting("leon") == "vanilla": + if self.get_setting("leon") == "vanilla": self.multiworld.get_location("The Maya Boss Room", self.player).place_locked_item( self.create_event("Defeat Ponce de Leon")) else: @@ -243,7 +239,7 @@ def _place_events(self): self.create_event("Defeat Ponce de Freon")) # Herodotus / Astrodotus - if self.setting("herodotus") == "vanilla": + if self.get_setting("herodotus") == "vanilla": self.multiworld.get_location("Land of Darkness Boss Room", self.player).place_locked_item( self.create_event("Defeat Herodotus")) else: diff --git a/worlds/rogue_legacy/docs/en_Rogue Legacy.md b/worlds/rogue_legacy/docs/en_Rogue Legacy.md index f8a166abf9a1..c91dc0de6f7a 100644 --- a/worlds/rogue_legacy/docs/en_Rogue Legacy.md +++ b/worlds/rogue_legacy/docs/en_Rogue Legacy.md @@ -3,7 +3,7 @@ ## Where is the settings page? The [player settings page for this game](../player-settings) contains most of the options you need to -configure and export a config file. Some settings can only be made via YAML, but an explaination can be found in the +configure and export a config file. Some settings can only be made in YAML, but an explanation can be found in the [template yaml here](../../../static/generated/configs/Rogue%20Legacy.yaml). ## What does randomization do to this game? @@ -13,7 +13,6 @@ upgrade screen, bosses, and some special individual locations. The goal is to be zone bosses and then defeat The Fountain. ## What items and locations get shuffled? - All the skill upgrades, class upgrades, runes packs, and equipment packs are shuffled in the manor upgrade screen, diary checks, chests and fairy chests, and boss rewards. Skill upgrades are also grouped in packs of 5 to make the finding of stats less of a chore. Runes and Equipment are also grouped together. @@ -24,7 +23,6 @@ Some additional locations that can contain items are the Jukebox, the Portraits, Any of the items which can be shuffled may also be placed into another player's world. It is possible to choose to limit certain items to your own world. - ## When the player receives an item, what happens? When the player receives an item, your character will hold the item above their head and display it to the world. It's @@ -33,4 +31,4 @@ good for business! ## What do I do if I encounter a bug with the game? Please reach out to Phar#4444 on Discord or you can drop a bug report on the -[GitHub page for Rogue Legacy Randomizer](https://github.com/ThePhar/RogueLegacyRandomizer/issues/new?assignees=&labels=bug&template=report-an-issue---.md&title=%5BIssue%5D). \ No newline at end of file +[GitHub page for Rogue Legacy Randomizer](https://github.com/ThePhar/RogueLegacyRandomizer/issues/new?assignees=&labels=bug&template=report-an-issue---.md&title=%5BIssue%5D). diff --git a/worlds/rogue_legacy/docs/rogue-legacy_en.md b/worlds/rogue_legacy/docs/rogue-legacy_en.md index decb484748f4..e513d0f0ca18 100644 --- a/worlds/rogue_legacy/docs/rogue-legacy_en.md +++ b/worlds/rogue_legacy/docs/rogue-legacy_en.md @@ -2,8 +2,14 @@ ## Required Software -- Rogue Legacy Randomizer from - the [Rogue Legacy Randomizer Releases Page](https://github.com/ThePhar/RogueLegacyRandomizer/releases) +- Rogue Legacy Randomizer from the + [Rogue Legacy Randomizer Releases Page](https://github.com/ThePhar/RogueLegacyRandomizer/releases) + +## Recommended Installation Instructions + +Please read the README file on the +[Rogue Legacy Randomizer GitHub](https://github.com/ThePhar/RogueLegacyRandomizer/blob/master/README.md) page for +up-to-date installation instructions. ## Configuring your YAML file @@ -27,9 +33,3 @@ provides an alternative one to the default values. Once you have entered the required values, you go to Connect and then select Confirm on the "Ready to Start" screen. Now you're off to start your legacy! - -## Recommended Installation Instructions - -Please read the README file on the -[Rogue Legacy Randomizer GitHub](https://github.com/ThePhar/RogueLegacyRandomizer/blob/master/README.md) page for up to -date installation instructions. From 78ee19de51d8db227f9751095e05e10d8fc7cfa9 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Wed, 7 Dec 2022 06:02:54 +0100 Subject: [PATCH 18/35] Pokemon R/B: always bind file handling to client (#1261) --- inno_setup.iss | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/inno_setup.iss b/inno_setup.iss index 5e7414d18130..1a4a6a56ba07 100644 --- a/inno_setup.iss +++ b/inno_setup.iss @@ -200,15 +200,15 @@ Root: HKCR; Subkey: "{#MyAppName}n64zpf"; ValueData: "Archip Root: HKCR; Subkey: "{#MyAppName}n64zpf\DefaultIcon"; ValueData: "{app}\ArchipelagoOoTClient.exe,0"; ValueType: string; ValueName: ""; Components: client/oot Root: HKCR; Subkey: "{#MyAppName}n64zpf\shell\open\command"; ValueData: """{app}\ArchipelagoOoTClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/oot -Root: HKCR; Subkey: ".apred"; ValueData: "{#MyAppName}pkmnrpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/pkmn/red -Root: HKCR; Subkey: "{#MyAppName}pkmnrpatch"; ValueData: "Archipelago Pokemon Red Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/pkmn/red -Root: HKCR; Subkey: "{#MyAppName}pkmnrpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoPokemonClient.exe,0"; ValueType: string; ValueName: ""; Components: client/pkmn/red -Root: HKCR; Subkey: "{#MyAppName}pkmnrpatch\shell\open\command"; ValueData: """{app}\ArchipelagoPokemonClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/pkmn/red - -Root: HKCR; Subkey: ".apblue"; ValueData: "{#MyAppName}pkmnbpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/pkmn/blue -Root: HKCR; Subkey: "{#MyAppName}pkmnbpatch"; ValueData: "Archipelago Pokemon Blue Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/pkmn/blue -Root: HKCR; Subkey: "{#MyAppName}pkmnbpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoPokemonClient.exe,0"; ValueType: string; ValueName: ""; Components: client/pkmn/blue -Root: HKCR; Subkey: "{#MyAppName}pkmnbpatch\shell\open\command"; ValueData: """{app}\ArchipelagoPokemonClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/pkmn/blue +Root: HKCR; Subkey: ".apred"; ValueData: "{#MyAppName}pkmnrpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/pkmn +Root: HKCR; Subkey: "{#MyAppName}pkmnrpatch"; ValueData: "Archipelago Pokemon Red Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/pkmn +Root: HKCR; Subkey: "{#MyAppName}pkmnrpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoPokemonClient.exe,0"; ValueType: string; ValueName: ""; Components: client/pkmn +Root: HKCR; Subkey: "{#MyAppName}pkmnrpatch\shell\open\command"; ValueData: """{app}\ArchipelagoPokemonClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/pkmn + +Root: HKCR; Subkey: ".apblue"; ValueData: "{#MyAppName}pkmnbpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/pkmn +Root: HKCR; Subkey: "{#MyAppName}pkmnbpatch"; ValueData: "Archipelago Pokemon Blue Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/pkmn +Root: HKCR; Subkey: "{#MyAppName}pkmnbpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoPokemonClient.exe,0"; ValueType: string; ValueName: ""; Components: client/pkmn +Root: HKCR; Subkey: "{#MyAppName}pkmnbpatch\shell\open\command"; ValueData: """{app}\ArchipelagoPokemonClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/pkmn Root: HKCR; Subkey: ".archipelago"; ValueData: "{#MyAppName}multidata"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: server Root: HKCR; Subkey: "{#MyAppName}multidata"; ValueData: "Archipelago Server Data"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: server From f5638552cc22cb832c00e82088ca7cbc22647a00 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Wed, 7 Dec 2022 06:12:56 +0100 Subject: [PATCH 19/35] Docs: add ClassVar marker to World class (#1224) From 449973687b970cdba1116ba313230d4ec7d2e6ae Mon Sep 17 00:00:00 2001 From: PoryGone <98504756+PoryGone@users.noreply.github.com> Date: Wed, 7 Dec 2022 00:20:02 -0500 Subject: [PATCH 20/35] SA2B: v2.0 Content Update (#1294) Changelog: Features: - Completely reworked mission progression system - Control of which mission types can be active per-gameplay-style - Control of how many missions are active per-gameplay-style - Mission order shuffle - Two new Chaos Emerald Hunt goals - `Chaos Emerald Hunt` involves finding the seven Chaos Emeralds and beating Green Hill - `FinalHazard Chaos Emerald Hunt` is the same, but with the FinalHazard fight at the end of Green Hill - New optional Location Checks - Keysanity (Chao Containers) - Whistlesanity (Animal Pipes and hidden whistle spots) - Beetlesanity (Destroying Gold Beetles) - Option to require clearing all active Cannon's Core Missions for access to the Biolizard fight in `Biolizard` goal - Hard Logic option - More Music Options - Option to use SADX music - New `Singularity` music shuffle option - Option to choose the Narrator theme - New Traps - Tiny Trap is now permanent within a level - Gravity Trap - Exposition Trap Quality of Life: - Significant revamp to Stage Select screen information conveyance - Icons are displayed for: - Relevant character's upgrades - Which location checks are active/checked - Chaos Emeralds found (if relevant) - Gate and Cannon's Core emblem costs - The above stage-specific info can also be viewed when paused in-level - The current mission is also displayed when paused - Emblem Symbol on Mission Select subscreen now only displays if a high enough rank has been gotten on that mission to send the location check - Hints including SA2B locations will now specify which Gate that level is located in - Save file now stores slot name to help prevent false location checks in the case of one player having multiple SA2B slots in the same seed - Chao Intermediate and Expert race sets are now swapped, per player feedback - Intermediate now includes Beginner + Challenge + Hero + Dark - Expert now includes Beginner + Challenge + Hero + Dark + Jewel - New mod config option for the color of the Message Queue text Bug Fixes: - Fixed bug where game stops properly tracking items after 127 have been received. - Several logic fixes - Game now refers to `Knuckles - Shovel Claws` correctly - Minor AP World code cleanup --- worlds/sa2b/Items.py | 24 +- worlds/sa2b/Locations.py | 374 ++++- worlds/sa2b/Missions.py | 327 +++++ worlds/sa2b/Names/ItemName.py | 19 +- worlds/sa2b/Names/LocationName.py | 615 +++++--- worlds/sa2b/Options.py | 363 ++++- worlds/sa2b/Regions.py | 467 +++++-- worlds/sa2b/Rules.py | 1234 +++++++++++++---- worlds/sa2b/__init__.py | 138 +- .../sa2b/docs/en_Sonic Adventure 2 Battle.md | 10 +- worlds/sa2b/docs/setup_en.md | 14 + 11 files changed, 2971 insertions(+), 614 deletions(-) create mode 100644 worlds/sa2b/Missions.py diff --git a/worlds/sa2b/Items.py b/worlds/sa2b/Items.py index d11178f5754d..904a854cfaa9 100644 --- a/worlds/sa2b/Items.py +++ b/worlds/sa2b/Items.py @@ -72,10 +72,23 @@ def __init__(self, name, classification: ItemClassification, code: int = None, p } trap_table = { - ItemName.omochao_trap: ItemData(0xFF0030, False, True), - ItemName.timestop_trap: ItemData(0xFF0031, False, True), - ItemName.confuse_trap: ItemData(0xFF0032, False, True), - ItemName.tiny_trap: ItemData(0xFF0033, False, True), + ItemName.omochao_trap: ItemData(0xFF0030, False, True), + ItemName.timestop_trap: ItemData(0xFF0031, False, True), + ItemName.confuse_trap: ItemData(0xFF0032, False, True), + ItemName.tiny_trap: ItemData(0xFF0033, False, True), + ItemName.gravity_trap: ItemData(0xFF0034, False, True), + ItemName.exposition_trap: ItemData(0xFF0035, False, True), + #ItemName.darkness_trap: ItemData(0xFF0036, False, True), +} + +emeralds_table = { + ItemName.white_emerald: ItemData(0xFF0040, True), + ItemName.red_emerald: ItemData(0xFF0041, True), + ItemName.cyan_emerald: ItemData(0xFF0042, True), + ItemName.purple_emerald: ItemData(0xFF0043, True), + ItemName.green_emerald: ItemData(0xFF0044, True), + ItemName.yellow_emerald: ItemData(0xFF0045, True), + ItemName.blue_emerald: ItemData(0xFF0046, True), } event_table = { @@ -88,10 +101,13 @@ def __init__(self, name, classification: ItemClassification, code: int = None, p **upgrades_table, **junk_table, **trap_table, + **emeralds_table, **event_table, } lookup_id_to_name: typing.Dict[int, str] = {data.code: item_name for item_name, data in item_table.items() if data.code} +item_groups: typing.Dict[str, str] = {"Chaos Emeralds": [item_name for item_name, data in emeralds_table.items()]} + ALTTPWorld.pedestal_credit_texts[item_table[ItemName.sonic_light_shoes].code] = "and the Soap Shoes" ALTTPWorld.pedestal_credit_texts[item_table[ItemName.shadow_air_shoes].code] = "and the Soap Shoes" diff --git a/worlds/sa2b/Locations.py b/worlds/sa2b/Locations.py index f5f4c79f9d71..0ce14877ea19 100644 --- a/worlds/sa2b/Locations.py +++ b/worlds/sa2b/Locations.py @@ -1,14 +1,15 @@ import typing -from BaseClasses import Location +from BaseClasses import Location, MultiWorld from .Names import LocationName +from .Missions import stage_name_prefixes, mission_orders class SA2BLocation(Location): game: str = "Sonic Adventure 2: Battle" -first_mission_location_table = { +mission_location_table = { LocationName.city_escape_1: 0xFF0000, LocationName.wild_canyon_1: 0xFF0001, LocationName.prison_lane_1: 0xFF0002, @@ -42,9 +43,8 @@ class SA2BLocation(Location): LocationName.final_chase_1: 0xFF001D, LocationName.cannon_core_1: 0xFF001E, -} -second_mission_location_table = { + LocationName.city_escape_2: 0xFF0020, LocationName.wild_canyon_2: 0xFF0021, LocationName.prison_lane_2: 0xFF0022, @@ -78,9 +78,8 @@ class SA2BLocation(Location): LocationName.final_chase_2: 0xFF003D, LocationName.cannon_core_2: 0xFF003E, -} -third_mission_location_table = { + LocationName.city_escape_3: 0xFF0040, LocationName.wild_canyon_3: 0xFF0041, LocationName.prison_lane_3: 0xFF0042, @@ -114,9 +113,8 @@ class SA2BLocation(Location): LocationName.final_chase_3: 0xFF005D, LocationName.cannon_core_3: 0xFF005E, -} -fourth_mission_location_table = { + LocationName.city_escape_4: 0xFF0060, LocationName.wild_canyon_4: 0xFF0061, LocationName.prison_lane_4: 0xFF0062, @@ -150,9 +148,8 @@ class SA2BLocation(Location): LocationName.final_chase_4: 0xFF007D, LocationName.cannon_core_4: 0xFF007E, -} -fifth_mission_location_table = { + LocationName.city_escape_5: 0xFF0080, LocationName.wild_canyon_5: 0xFF0081, LocationName.prison_lane_5: 0xFF0082, @@ -220,6 +217,292 @@ class SA2BLocation(Location): LocationName.final_chase_upgrade: 0xFF00BD, } +chao_key_location_table = { + LocationName.city_escape_chao_1: 0xFF0400, + LocationName.wild_canyon_chao_1: 0xFF0401, + LocationName.prison_lane_chao_1: 0xFF0402, + LocationName.metal_harbor_chao_1: 0xFF0403, + LocationName.green_forest_chao_1: 0xFF0404, + LocationName.pumpkin_hill_chao_1: 0xFF0405, + LocationName.mission_street_chao_1: 0xFF0406, + LocationName.aquatic_mine_chao_1: 0xFF0407, + LocationName.hidden_base_chao_1: 0xFF0409, + LocationName.pyramid_cave_chao_1: 0xFF040A, + LocationName.death_chamber_chao_1: 0xFF040B, + LocationName.eternal_engine_chao_1: 0xFF040C, + LocationName.meteor_herd_chao_1: 0xFF040D, + LocationName.crazy_gadget_chao_1: 0xFF040E, + LocationName.final_rush_chao_1: 0xFF040F, + + LocationName.iron_gate_chao_1: 0xFF0410, + LocationName.dry_lagoon_chao_1: 0xFF0411, + LocationName.sand_ocean_chao_1: 0xFF0412, + LocationName.radical_highway_chao_1: 0xFF0413, + LocationName.egg_quarters_chao_1: 0xFF0414, + LocationName.lost_colony_chao_1: 0xFF0415, + LocationName.weapons_bed_chao_1: 0xFF0416, + LocationName.security_hall_chao_1: 0xFF0417, + LocationName.white_jungle_chao_1: 0xFF0418, + LocationName.sky_rail_chao_1: 0xFF041A, + LocationName.mad_space_chao_1: 0xFF041B, + LocationName.cosmic_wall_chao_1: 0xFF041C, + LocationName.final_chase_chao_1: 0xFF041D, + + LocationName.cannon_core_chao_1: 0xFF041E, + + LocationName.city_escape_chao_2: 0xFF0420, + LocationName.wild_canyon_chao_2: 0xFF0421, + LocationName.prison_lane_chao_2: 0xFF0422, + LocationName.metal_harbor_chao_2: 0xFF0423, + LocationName.green_forest_chao_2: 0xFF0424, + LocationName.pumpkin_hill_chao_2: 0xFF0425, + LocationName.mission_street_chao_2: 0xFF0426, + LocationName.aquatic_mine_chao_2: 0xFF0427, + LocationName.hidden_base_chao_2: 0xFF0429, + LocationName.pyramid_cave_chao_2: 0xFF042A, + LocationName.death_chamber_chao_2: 0xFF042B, + LocationName.eternal_engine_chao_2: 0xFF042C, + LocationName.meteor_herd_chao_2: 0xFF042D, + LocationName.crazy_gadget_chao_2: 0xFF042E, + LocationName.final_rush_chao_2: 0xFF042F, + + LocationName.iron_gate_chao_2: 0xFF0430, + LocationName.dry_lagoon_chao_2: 0xFF0431, + LocationName.sand_ocean_chao_2: 0xFF0432, + LocationName.radical_highway_chao_2: 0xFF0433, + LocationName.egg_quarters_chao_2: 0xFF0434, + LocationName.lost_colony_chao_2: 0xFF0435, + LocationName.weapons_bed_chao_2: 0xFF0436, + LocationName.security_hall_chao_2: 0xFF0437, + LocationName.white_jungle_chao_2: 0xFF0438, + LocationName.sky_rail_chao_2: 0xFF043A, + LocationName.mad_space_chao_2: 0xFF043B, + LocationName.cosmic_wall_chao_2: 0xFF043C, + LocationName.final_chase_chao_2: 0xFF043D, + + LocationName.cannon_core_chao_2: 0xFF043E, + + LocationName.city_escape_chao_3: 0xFF0440, + LocationName.wild_canyon_chao_3: 0xFF0441, + LocationName.prison_lane_chao_3: 0xFF0442, + LocationName.metal_harbor_chao_3: 0xFF0443, + LocationName.green_forest_chao_3: 0xFF0444, + LocationName.pumpkin_hill_chao_3: 0xFF0445, + LocationName.mission_street_chao_3: 0xFF0446, + LocationName.aquatic_mine_chao_3: 0xFF0447, + LocationName.pyramid_cave_chao_3: 0xFF044A, + LocationName.death_chamber_chao_3: 0xFF044B, + LocationName.eternal_engine_chao_3: 0xFF044C, + LocationName.meteor_herd_chao_3: 0xFF044D, + LocationName.crazy_gadget_chao_3: 0xFF044E, + LocationName.final_rush_chao_3: 0xFF044F, + + LocationName.iron_gate_chao_3: 0xFF0450, + LocationName.dry_lagoon_chao_3: 0xFF0451, + LocationName.sand_ocean_chao_3: 0xFF0452, + LocationName.radical_highway_chao_3: 0xFF0453, + LocationName.egg_quarters_chao_3: 0xFF0454, + LocationName.lost_colony_chao_3: 0xFF0455, + LocationName.weapons_bed_chao_3: 0xFF0456, + LocationName.security_hall_chao_3: 0xFF0457, + LocationName.white_jungle_chao_3: 0xFF0458, + LocationName.sky_rail_chao_3: 0xFF045A, + LocationName.mad_space_chao_3: 0xFF045B, + LocationName.cosmic_wall_chao_3: 0xFF045C, + LocationName.final_chase_chao_3: 0xFF045D, + + LocationName.cannon_core_chao_3: 0xFF045E, +} + +pipe_location_table = { + LocationName.city_escape_pipe_1: 0xFF0500, + LocationName.wild_canyon_pipe_1: 0xFF0501, + LocationName.prison_lane_pipe_1: 0xFF0502, + LocationName.metal_harbor_pipe_1: 0xFF0503, + LocationName.green_forest_pipe_1: 0xFF0504, + LocationName.pumpkin_hill_pipe_1: 0xFF0505, + LocationName.mission_street_pipe_1: 0xFF0506, + LocationName.aquatic_mine_pipe_1: 0xFF0507, + LocationName.hidden_base_pipe_1: 0xFF0509, + LocationName.pyramid_cave_pipe_1: 0xFF050A, + LocationName.death_chamber_pipe_1: 0xFF050B, + LocationName.eternal_engine_pipe_1: 0xFF050C, + LocationName.meteor_herd_pipe_1: 0xFF050D, + LocationName.crazy_gadget_pipe_1: 0xFF050E, + LocationName.final_rush_pipe_1: 0xFF050F, + + LocationName.iron_gate_pipe_1: 0xFF0510, + LocationName.dry_lagoon_pipe_1: 0xFF0511, + LocationName.sand_ocean_pipe_1: 0xFF0512, + LocationName.radical_highway_pipe_1: 0xFF0513, + LocationName.egg_quarters_pipe_1: 0xFF0514, + LocationName.lost_colony_pipe_1: 0xFF0515, + LocationName.weapons_bed_pipe_1: 0xFF0516, + LocationName.security_hall_pipe_1: 0xFF0517, + LocationName.white_jungle_pipe_1: 0xFF0518, + LocationName.sky_rail_pipe_1: 0xFF051A, + LocationName.mad_space_pipe_1: 0xFF051B, + LocationName.cosmic_wall_pipe_1: 0xFF051C, + LocationName.final_chase_pipe_1: 0xFF051D, + + LocationName.cannon_core_pipe_1: 0xFF051E, + + LocationName.city_escape_pipe_2: 0xFF0520, + LocationName.wild_canyon_pipe_2: 0xFF0521, + LocationName.prison_lane_pipe_2: 0xFF0522, + LocationName.green_forest_pipe_2: 0xFF0524, + LocationName.mission_street_pipe_2: 0xFF0526, + LocationName.aquatic_mine_pipe_2: 0xFF0527, + LocationName.hidden_base_pipe_2: 0xFF0529, + LocationName.pyramid_cave_pipe_2: 0xFF052A, + LocationName.death_chamber_pipe_2: 0xFF052B, + LocationName.eternal_engine_pipe_2: 0xFF052C, + LocationName.meteor_herd_pipe_2: 0xFF052D, + LocationName.crazy_gadget_pipe_2: 0xFF052E, + LocationName.final_rush_pipe_2: 0xFF052F, + + LocationName.iron_gate_pipe_2: 0xFF0530, + LocationName.sand_ocean_pipe_2: 0xFF0532, + LocationName.radical_highway_pipe_2: 0xFF0533, + LocationName.egg_quarters_pipe_2: 0xFF0534, + LocationName.lost_colony_pipe_2: 0xFF0535, + LocationName.weapons_bed_pipe_2: 0xFF0536, + LocationName.white_jungle_pipe_2: 0xFF0538, + LocationName.sky_rail_pipe_2: 0xFF053A, + LocationName.mad_space_pipe_2: 0xFF053B, + LocationName.cosmic_wall_pipe_2: 0xFF053C, + LocationName.final_chase_pipe_2: 0xFF053D, + + LocationName.cannon_core_pipe_2: 0xFF053E, + + LocationName.city_escape_pipe_3: 0xFF0540, + LocationName.wild_canyon_pipe_3: 0xFF0541, + LocationName.prison_lane_pipe_3: 0xFF0542, + LocationName.mission_street_pipe_3: 0xFF0546, + LocationName.aquatic_mine_pipe_3: 0xFF0547, + LocationName.hidden_base_pipe_3: 0xFF0549, + LocationName.pyramid_cave_pipe_3: 0xFF054A, + LocationName.death_chamber_pipe_3: 0xFF054B, + LocationName.eternal_engine_pipe_3: 0xFF054C, + LocationName.meteor_herd_pipe_3: 0xFF054D, + LocationName.crazy_gadget_pipe_3: 0xFF054E, + + LocationName.iron_gate_pipe_3: 0xFF0550, + LocationName.sand_ocean_pipe_3: 0xFF0552, + LocationName.radical_highway_pipe_3: 0xFF0553, + LocationName.weapons_bed_pipe_3: 0xFF0556, + LocationName.white_jungle_pipe_3: 0xFF0558, + LocationName.sky_rail_pipe_3: 0xFF055A, + LocationName.mad_space_pipe_3: 0xFF055B, + LocationName.cosmic_wall_pipe_3: 0xFF055C, + LocationName.final_chase_pipe_3: 0xFF055D, + + LocationName.cannon_core_pipe_3: 0xFF055E, + + LocationName.city_escape_pipe_4: 0xFF0560, + LocationName.hidden_base_pipe_4: 0xFF0569, + LocationName.pyramid_cave_pipe_4: 0xFF056A, + LocationName.eternal_engine_pipe_4: 0xFF056C, + LocationName.crazy_gadget_pipe_4: 0xFF056E, + + LocationName.iron_gate_pipe_4: 0xFF0570, + LocationName.sand_ocean_pipe_4: 0xFF0572, + LocationName.weapons_bed_pipe_4: 0xFF0576, + LocationName.white_jungle_pipe_4: 0xFF0578, + LocationName.sky_rail_pipe_4: 0xFF057A, + LocationName.mad_space_pipe_4: 0xFF057B, + LocationName.cosmic_wall_pipe_4: 0xFF057C, + + LocationName.cannon_core_pipe_4: 0xFF057E, + + LocationName.hidden_base_pipe_5: 0xFF0589, + LocationName.eternal_engine_pipe_5: 0xFF058C, + + LocationName.iron_gate_pipe_5: 0xFF0590, + LocationName.sand_ocean_pipe_5: 0xFF0592, + LocationName.weapons_bed_pipe_5: 0xFF0596, + LocationName.sky_rail_pipe_5: 0xFF059A, + LocationName.cosmic_wall_pipe_5: 0xFF059C, + + LocationName.cannon_core_pipe_5: 0xFF059E, + + LocationName.sky_rail_pipe_6: 0xFF05BA, +} + +hidden_whistle_location_table = { + LocationName.city_escape_hidden_1: 0xFF0700, + LocationName.prison_lane_hidden_1: 0xFF0702, + LocationName.green_forest_hidden_1: 0xFF0704, + LocationName.pumpkin_hill_hidden_1: 0xFF0705, + LocationName.mission_street_hidden_1: 0xFF0706, + LocationName.death_chamber_hidden_1: 0xFF070B, + LocationName.crazy_gadget_hidden_1: 0xFF070E, + + LocationName.dry_lagoon_hidden_1: 0xFF0711, + LocationName.radical_highway_hidden_1: 0xFF0713, + LocationName.egg_quarters_hidden_1: 0xFF0714, + LocationName.lost_colony_hidden_1: 0xFF0715, + LocationName.security_hall_hidden_1: 0xFF0717, + LocationName.white_jungle_hidden_1: 0xFF0718, + + LocationName.cannon_core_hidden_1: 0xFF071E, + + LocationName.city_escape_hidden_2: 0xFF0720, + LocationName.prison_lane_hidden_2: 0xFF0722, + LocationName.green_forest_hidden_2: 0xFF0724, + LocationName.mission_street_hidden_2: 0xFF0726, + LocationName.death_chamber_hidden_2: 0xFF072B, + + LocationName.radical_highway_hidden_2: 0xFF0733, + LocationName.egg_quarters_hidden_2: 0xFF0734, + LocationName.white_jungle_hidden_2: 0xFF0738, + + LocationName.city_escape_hidden_3: 0xFF0740, + LocationName.prison_lane_hidden_3: 0xFF0742, + LocationName.green_forest_hidden_3: 0xFF0744, + LocationName.mission_street_hidden_3: 0xFF0746, + + LocationName.radical_highway_hidden_3: 0xFF0753, + LocationName.white_jungle_hidden_3: 0xFF0758, + + LocationName.city_escape_hidden_4: 0xFF0760, + LocationName.green_forest_hidden_4: 0xFF0764, + + LocationName.city_escape_hidden_5: 0xFF0780, +} + +beetle_location_table = { + LocationName.city_escape_beetle: 0xFF0600, + LocationName.wild_canyon_beetle: 0xFF0601, + LocationName.prison_lane_beetle: 0xFF0602, + LocationName.metal_harbor_beetle: 0xFF0603, + LocationName.green_forest_beetle: 0xFF0604, + LocationName.mission_street_beetle: 0xFF0606, + LocationName.aquatic_mine_beetle: 0xFF0607, + LocationName.hidden_base_beetle: 0xFF0609, + LocationName.pyramid_cave_beetle: 0xFF060A, + LocationName.death_chamber_beetle: 0xFF060B, + LocationName.eternal_engine_beetle: 0xFF060C, + LocationName.meteor_herd_beetle: 0xFF060D, + LocationName.crazy_gadget_beetle: 0xFF060E, + LocationName.final_rush_beetle: 0xFF060F, + + LocationName.iron_gate_beetle: 0xFF0610, + LocationName.dry_lagoon_beetle: 0xFF0611, + LocationName.sand_ocean_beetle: 0xFF0612, + LocationName.radical_highway_beetle: 0xFF0613, + LocationName.egg_quarters_beetle: 0xFF0614, + LocationName.lost_colony_beetle: 0xFF0615, + LocationName.security_hall_beetle: 0xFF0617, + LocationName.white_jungle_beetle: 0xFF0618, + LocationName.sky_rail_beetle: 0xFF061A, + LocationName.mad_space_beetle: 0xFF061B, + LocationName.cosmic_wall_beetle: 0xFF061C, + LocationName.final_chase_beetle: 0xFF061D, + + LocationName.cannon_core_beetle: 0xFF061E, +} + boss_gate_location_table = { LocationName.gate_1_boss: 0xFF0100, LocationName.gate_2_boss: 0xFF0101, @@ -308,23 +591,33 @@ class SA2BLocation(Location): LocationName.chao_super_karate: 0xFF0303, } -other_location_table = { - # LocationName.green_hill: 0xFF001F, - LocationName.biolizard: 0xFF003F, +green_hill_location_table = { + LocationName.green_hill: 0xFF001F, +} + +green_hill_chao_location_table = { + LocationName.green_hill_chao_1: 0xFF041F, +} + +final_boss_location_table = { + # LocationName.biolizard: 0xFF003F, + LocationName.finalhazard: 0xFF005F, } all_locations = { - **first_mission_location_table, - **second_mission_location_table, - **third_mission_location_table, - **fourth_mission_location_table, - **fifth_mission_location_table, + **mission_location_table, **upgrade_location_table, **boss_gate_location_table, + **chao_key_location_table, + **pipe_location_table, + **hidden_whistle_location_table, + **beetle_location_table, **chao_garden_beginner_location_table, **chao_garden_intermediate_location_table, **chao_garden_expert_location_table, - **other_location_table, + **green_hill_location_table, + **green_hill_chao_location_table, + **final_boss_location_table, } boss_gate_set = [ @@ -367,26 +660,45 @@ class SA2BLocation(Location): ] -def setup_locations(world, player: int): +def setup_locations(world: MultiWorld, player: int, mission_map: typing.Dict[int, int], mission_count_map: typing.Dict[int, int]): location_table = {} chao_location_table = {} - location_table.update({**first_mission_location_table}) - if world.include_missions[player].value >= 2: - location_table.update({**second_mission_location_table}) - if world.include_missions[player].value >= 3: - location_table.update({**third_mission_location_table}) + for i in range(31): + mission_count = mission_count_map[i] + mission_order: typing.List[int] = mission_orders[mission_map[i]] + stage_prefix: str = stage_name_prefixes[i] - if world.include_missions[player].value >= 4: - location_table.update({**fourth_mission_location_table}) - - if world.include_missions[player].value >= 5: - location_table.update({**fifth_mission_location_table}) + for j in range(mission_count): + mission_number = mission_order[j] + location_name: str = stage_prefix + str(mission_number) + location_table[location_name] = mission_location_table[location_name] location_table.update({**upgrade_location_table}) - location_table.update({**other_location_table}) + if world.keysanity[player]: + location_table.update({**chao_key_location_table}) + + if world.whistlesanity[player].value == 1: + location_table.update({**pipe_location_table}) + elif world.whistlesanity[player].value == 2: + location_table.update({**hidden_whistle_location_table}) + elif world.whistlesanity[player].value == 3: + location_table.update({**pipe_location_table}) + location_table.update({**hidden_whistle_location_table}) + + if world.beetlesanity[player]: + location_table.update({**beetle_location_table}) + + if world.goal[player].value == 0 or world.goal[player].value == 2: + location_table.update({**final_boss_location_table}) + + if world.goal[player].value == 1 or world.goal[player].value == 2: + location_table.update({**green_hill_location_table}) + + if world.keysanity[player]: + location_table.update({**green_hill_chao_location_table}) if world.chao_garden_difficulty[player].value >= 1: chao_location_table.update({**chao_garden_beginner_location_table}) diff --git a/worlds/sa2b/Missions.py b/worlds/sa2b/Missions.py new file mode 100644 index 000000000000..d9767586a627 --- /dev/null +++ b/worlds/sa2b/Missions.py @@ -0,0 +1,327 @@ +import typing +import copy + +from BaseClasses import MultiWorld + + +mission_orders: typing.List[typing.List[int]] = [ + [1, 2, 3, 4, 5], + [1, 2, 3, 5, 4], + [1, 2, 4, 3, 5], + [1, 2, 4, 5, 3], + [1, 2, 5, 3, 4], + [1, 2, 5, 4, 3], + + [1, 3, 2, 4, 5], + [1, 3, 2, 5, 4], + [1, 3, 4, 2, 5], + [1, 3, 4, 5, 2], + [1, 3, 5, 2, 4], + [1, 3, 5, 4, 2], + + [1, 4, 2, 3, 5], + [1, 4, 2, 5, 3], + [1, 4, 3, 2, 5], + [1, 4, 3, 5, 2], + [1, 4, 5, 2, 3], + [1, 4, 5, 3, 2], + + [1, 5, 2, 3, 4], + [1, 5, 2, 4, 3], + [1, 5, 3, 2, 4], + [1, 5, 3, 4, 2], + [1, 5, 4, 2, 3], + [1, 5, 4, 3, 2], + + [2, 1, 3, 4, 5], + [2, 1, 3, 5, 4], + [2, 1, 4, 3, 5], + [2, 1, 4, 5, 3], + [2, 1, 5, 3, 4], + [2, 1, 5, 4, 3], + + [2, 3, 1, 4, 5], + [2, 3, 1, 5, 4], + [2, 3, 4, 1, 5], + [2, 3, 4, 5, 1], + [2, 3, 5, 1, 4], + [2, 3, 5, 4, 1], + + [2, 4, 1, 3, 5], + [2, 4, 1, 5, 3], + [2, 4, 3, 1, 5], + [2, 4, 3, 5, 1], + [2, 4, 5, 1, 3], + [2, 4, 5, 3, 1], + + [2, 5, 1, 3, 4], + [2, 5, 1, 4, 3], + [2, 5, 3, 1, 4], + [2, 5, 3, 4, 1], + [2, 5, 4, 1, 3], + [2, 5, 4, 3, 1], + + [3, 1, 2, 4, 5], + [3, 1, 2, 5, 4], + [3, 1, 4, 2, 5], + [3, 1, 4, 5, 2], + [3, 1, 5, 4, 2], + [3, 1, 5, 2, 4], + + [3, 2, 1, 4, 5], + [3, 2, 1, 5, 4], + [3, 2, 4, 1, 5], + [3, 2, 4, 5, 1], + [3, 2, 5, 1, 4], + [3, 2, 5, 4, 1], + + [3, 4, 1, 2, 5], + [3, 4, 1, 5, 2], + [3, 4, 2, 1, 5], + [3, 4, 2, 5, 1], + [3, 4, 5, 1, 2], + [3, 4, 5, 2, 1], + + [3, 5, 1, 4, 2], + [3, 5, 1, 2, 4], + [3, 5, 2, 1, 4], + [3, 5, 2, 4, 1], + [3, 5, 4, 1, 2], + [3, 5, 4, 2, 1], + + [4, 1, 2, 3, 5], + [4, 1, 2, 5, 3], + [4, 1, 3, 2, 5], + [4, 1, 3, 5, 2], + [4, 1, 5, 3, 2], + [4, 1, 5, 2, 3], + + [4, 2, 1, 3, 5], + [4, 2, 1, 5, 3], + [4, 2, 3, 1, 5], + [4, 2, 3, 5, 1], + [4, 2, 5, 1, 3], + [4, 2, 5, 3, 1], + + [4, 3, 1, 2, 5], + [4, 3, 1, 5, 2], + [4, 3, 2, 1, 5], + [4, 3, 2, 5, 1], + [4, 3, 5, 1, 2], + [4, 3, 5, 2, 1], + + [4, 5, 1, 3, 2], + [4, 5, 1, 2, 3], + [4, 5, 2, 1, 3], + [4, 5, 2, 3, 1], + [4, 5, 3, 1, 2], + [4, 5, 3, 2, 1], +] + +### 0: Speed +### 1: Mech +### 2: Hunt +### 3: Kart +### 4: Cannon's Core +level_styles: typing.List[int] = [ + 0, + 2, + 1, + 0, + 0, + 2, + 1, + 2, + 3, + 1, + 0, + 2, + 1, + 2, + 0, + 0, + + 1, + 2, + 1, + 0, + 2, + 1, + 1, + 2, + 0, + 3, + 0, + 2, + 1, + 0, + + 4, +] + +stage_name_prefixes: typing.List[str] = [ + "City Escape - ", + "Wild Canyon - ", + "Prison Lane - ", + "Metal Harbor - ", + "Green Forest - ", + "Pumpkin Hill - ", + "Mission Street - ", + "Aquatic Mine - ", + "Route 101 - ", + "Hidden Base - ", + "Pyramid Cave - ", + "Death Chamber - ", + "Eternal Engine - ", + "Meteor Herd - ", + "Crazy Gadget - ", + "Final Rush - ", + "Iron Gate - ", + "Dry Lagoon - ", + "Sand Ocean - ", + "Radical Highway - ", + "Egg Quarters - ", + "Lost Colony - ", + "Weapons Bed - ", + "Security Hall - ", + "White Jungle - ", + "Route 280 - ", + "Sky Rail - ", + "Mad Space - ", + "Cosmic Wall - ", + "Final Chase - ", + "Cannon Core - ", +] + +def get_mission_count_table(multiworld: MultiWorld, player: int): + speed_active_missions = 1 + mech_active_missions = 1 + hunt_active_missions = 1 + kart_active_missions = 1 + cannons_core_active_missions = 1 + + for i in range(2,6): + if getattr(multiworld, "speed_mission_" + str(i), None)[player]: + speed_active_missions += 1 + + if getattr(multiworld, "mech_mission_" + str(i), None)[player]: + mech_active_missions += 1 + + if getattr(multiworld, "hunt_mission_" + str(i), None)[player]: + hunt_active_missions += 1 + + if getattr(multiworld, "kart_mission_" + str(i), None)[player]: + kart_active_missions += 1 + + if getattr(multiworld, "cannons_core_mission_" + str(i), None)[player]: + cannons_core_active_missions += 1 + + speed_active_missions = min(speed_active_missions, multiworld.speed_mission_count[player].value) + mech_active_missions = min(mech_active_missions, multiworld.mech_mission_count[player].value) + hunt_active_missions = min(hunt_active_missions, multiworld.hunt_mission_count[player].value) + kart_active_missions = min(kart_active_missions, multiworld.kart_mission_count[player].value) + cannons_core_active_missions = min(cannons_core_active_missions, multiworld.cannons_core_mission_count[player].value) + + active_missions: typing.List[typing.List[int]] = [ + speed_active_missions, + mech_active_missions, + hunt_active_missions, + kart_active_missions, + cannons_core_active_missions + ] + + mission_count_table: typing.Dict[int, int] = {} + + for level in range(31): + level_style = level_styles[level] + level_mission_count = active_missions[level_style] + mission_count_table[level] = level_mission_count + + return mission_count_table + + +def get_mission_table(multiworld: MultiWorld, player: int): + mission_table: typing.Dict[int, int] = {} + + speed_active_missions: typing.List[int] = [1] + mech_active_missions: typing.List[int] = [1] + hunt_active_missions: typing.List[int] = [1] + kart_active_missions: typing.List[int] = [1] + cannons_core_active_missions: typing.List[int] = [1] + + # Add included missions + for i in range(2,6): + if getattr(multiworld, "speed_mission_" + str(i), None)[player]: + speed_active_missions.append(i) + + if getattr(multiworld, "mech_mission_" + str(i), None)[player]: + mech_active_missions.append(i) + + if getattr(multiworld, "hunt_mission_" + str(i), None)[player]: + hunt_active_missions.append(i) + + if getattr(multiworld, "kart_mission_" + str(i), None)[player]: + kart_active_missions.append(i) + + if getattr(multiworld, "cannons_core_mission_" + str(i), None)[player]: + cannons_core_active_missions.append(i) + + active_missions: typing.List[typing.List[int]] = [ + speed_active_missions, + mech_active_missions, + hunt_active_missions, + kart_active_missions, + cannons_core_active_missions + ] + + for level in range(31): + level_style = level_styles[level] + + level_active_missions: typing.List[int] = copy.deepcopy(active_missions[level_style]) + level_chosen_missions: typing.List[int] = [] + + # The first mission must be M1, M2, or M4 + first_mission = 1 + + if multiworld.mission_shuffle[player]: + first_mission = multiworld.random.choice([mission for mission in level_active_missions if mission in [1, 2, 3, 4]]) + + level_active_missions.remove(first_mission) + + # Place Active Missions in the chosen mission list + for mission in level_active_missions: + if mission not in level_chosen_missions: + level_chosen_missions.append(mission) + + if multiworld.mission_shuffle[player]: + multiworld.random.shuffle(level_chosen_missions) + + level_chosen_missions.insert(0, first_mission) + + # Fill in the non-included missions + for i in range(2,6): + if i not in level_chosen_missions: + level_chosen_missions.append(i) + + # Determine which mission order index we have, for conveying to the mod + for i in range(len(mission_orders)): + if mission_orders[i] == level_chosen_missions: + level_mission_index = i + break + + mission_table[level] = level_mission_index + + return mission_table + + +def get_first_and_last_cannons_core_missions(mission_map: typing.Dict[int, int], mission_count_map: typing.Dict[int, int]): + mission_count = mission_count_map[30] + mission_order: typing.List[int] = mission_orders[mission_map[30]] + stage_prefix: str = stage_name_prefixes[30] + + first_mission_number = mission_order[0] + last_mission_number = mission_order[mission_count - 1] + first_location_name: str = stage_prefix + str(first_mission_number) + last_location_name: str = stage_prefix + str(last_mission_number) + + return first_location_name, last_location_name diff --git a/worlds/sa2b/Names/ItemName.py b/worlds/sa2b/Names/ItemName.py index 32eaf5efea90..270b113383cf 100644 --- a/worlds/sa2b/Names/ItemName.py +++ b/worlds/sa2b/Names/ItemName.py @@ -44,9 +44,20 @@ magnetic_shield = "Magnetic Shield" invincibility = "Invincibility" -omochao_trap = "OmoTrap" -timestop_trap = "Chaos Control Trap" -confuse_trap = "Confusion Trap" -tiny_trap = "Tiny Trap" +omochao_trap = "OmoTrap" +timestop_trap = "Chaos Control Trap" +confuse_trap = "Confusion Trap" +tiny_trap = "Tiny Trap" +gravity_trap = "Gravity Trap" +exposition_trap = "Exposition Trap" +darkness_trap = "Darkness Trap" + +white_emerald = "White Chaos Emerald" +red_emerald = "Red Chaos Emerald" +cyan_emerald = "Cyan Chaos Emerald" +purple_emerald = "Purple Chaos Emerald" +green_emerald = "Green Chaos Emerald" +yellow_emerald = "Yellow Chaos Emerald" +blue_emerald = "Blue Chaos Emerald" maria = "What Maria Wanted" diff --git a/worlds/sa2b/Names/LocationName.py b/worlds/sa2b/Names/LocationName.py index 8d55d5e45465..0bb0dc6508e6 100644 --- a/worlds/sa2b/Names/LocationName.py +++ b/worlds/sa2b/Names/LocationName.py @@ -1,200 +1,441 @@ # Sonic Mission Definitions -city_escape_1 = "City Escape - 1" -city_escape_2 = "City Escape - 2" -city_escape_3 = "City Escape - 3" -city_escape_4 = "City Escape - 4" -city_escape_5 = "City Escape - 5" -city_escape_upgrade = "City Escape - Upgrade" -metal_harbor_1 = "Metal Harbor - 1" -metal_harbor_2 = "Metal Harbor - 2" -metal_harbor_3 = "Metal Harbor - 3" -metal_harbor_4 = "Metal Harbor - 4" -metal_harbor_5 = "Metal Harbor - 5" -metal_harbor_upgrade = "Metal Harbor - Upgrade" -green_forest_1 = "Green Forest - 1" -green_forest_2 = "Green Forest - 2" -green_forest_3 = "Green Forest - 3" -green_forest_4 = "Green Forest - 4" -green_forest_5 = "Green Forest - 5" -green_forest_upgrade = "Green Forest - Upgrade" -pyramid_cave_1 = "Pyramid Cave - 1" -pyramid_cave_2 = "Pyramid Cave - 2" -pyramid_cave_3 = "Pyramid Cave - 3" -pyramid_cave_4 = "Pyramid Cave - 4" -pyramid_cave_5 = "Pyramid Cave - 5" -pyramid_cave_upgrade = "Pyramid Cave - Upgrade" -crazy_gadget_1 = "Crazy Gadget - 1" -crazy_gadget_2 = "Crazy Gadget - 2" -crazy_gadget_3 = "Crazy Gadget - 3" -crazy_gadget_4 = "Crazy Gadget - 4" -crazy_gadget_5 = "Crazy Gadget - 5" -crazy_gadget_upgrade = "Crazy Gadget - Upgrade" -final_rush_1 = "Final Rush - 1" -final_rush_2 = "Final Rush - 2" -final_rush_3 = "Final Rush - 3" -final_rush_4 = "Final Rush - 4" -final_rush_5 = "Final Rush - 5" -final_rush_upgrade = "Final Rush - Upgrade" +city_escape_1 = "City Escape - 1" +city_escape_2 = "City Escape - 2" +city_escape_3 = "City Escape - 3" +city_escape_4 = "City Escape - 4" +city_escape_5 = "City Escape - 5" +city_escape_chao_1 = "City Escape - Chao Key 1" +city_escape_chao_2 = "City Escape - Chao Key 2" +city_escape_chao_3 = "City Escape - Chao Key 3" +city_escape_pipe_1 = "City Escape - Pipe 1" +city_escape_pipe_2 = "City Escape - Pipe 2" +city_escape_pipe_3 = "City Escape - Pipe 3" +city_escape_pipe_4 = "City Escape - Pipe 4" +city_escape_hidden_1 = "City Escape - Hidden 1" +city_escape_hidden_2 = "City Escape - Hidden 2" +city_escape_hidden_3 = "City Escape - Hidden 3" +city_escape_hidden_4 = "City Escape - Hidden 4" +city_escape_hidden_5 = "City Escape - Hidden 5" +city_escape_beetle = "City Escape - Gold Beetle" +city_escape_upgrade = "City Escape - Upgrade" +metal_harbor_1 = "Metal Harbor - 1" +metal_harbor_2 = "Metal Harbor - 2" +metal_harbor_3 = "Metal Harbor - 3" +metal_harbor_4 = "Metal Harbor - 4" +metal_harbor_5 = "Metal Harbor - 5" +metal_harbor_chao_1 = "Metal Harbor - Chao Key 1" +metal_harbor_chao_2 = "Metal Harbor - Chao Key 2" +metal_harbor_chao_3 = "Metal Harbor - Chao Key 3" +metal_harbor_pipe_1 = "Metal Harbor - Pipe 1" +metal_harbor_beetle = "Metal Harbor - Gold Beetle" +metal_harbor_upgrade = "Metal Harbor - Upgrade" +green_forest_1 = "Green Forest - 1" +green_forest_2 = "Green Forest - 2" +green_forest_3 = "Green Forest - 3" +green_forest_4 = "Green Forest - 4" +green_forest_5 = "Green Forest - 5" +green_forest_chao_1 = "Green Forest - Chao Key 1" +green_forest_chao_2 = "Green Forest - Chao Key 2" +green_forest_chao_3 = "Green Forest - Chao Key 3" +green_forest_pipe_1 = "Green Forest - Pipe 1" +green_forest_pipe_2 = "Green Forest - Pipe 2" +green_forest_hidden_1 = "Green Forest - Hidden 1" +green_forest_hidden_2 = "Green Forest - Hidden 2" +green_forest_hidden_3 = "Green Forest - Hidden 3" +green_forest_hidden_4 = "Green Forest - Hidden 4" +green_forest_beetle = "Green Forest - Gold Beetle" +green_forest_upgrade = "Green Forest - Upgrade" +pyramid_cave_1 = "Pyramid Cave - 1" +pyramid_cave_2 = "Pyramid Cave - 2" +pyramid_cave_3 = "Pyramid Cave - 3" +pyramid_cave_4 = "Pyramid Cave - 4" +pyramid_cave_5 = "Pyramid Cave - 5" +pyramid_cave_chao_1 = "Pyramid Cave - Chao Key 1" +pyramid_cave_chao_2 = "Pyramid Cave - Chao Key 2" +pyramid_cave_chao_3 = "Pyramid Cave - Chao Key 3" +pyramid_cave_pipe_1 = "Pyramid Cave - Pipe 1" +pyramid_cave_pipe_2 = "Pyramid Cave - Pipe 2" +pyramid_cave_pipe_3 = "Pyramid Cave - Pipe 3" +pyramid_cave_pipe_4 = "Pyramid Cave - Pipe 4" +pyramid_cave_beetle = "Pyramid Cave - Gold Beetle" +pyramid_cave_upgrade = "Pyramid Cave - Upgrade" +crazy_gadget_1 = "Crazy Gadget - 1" +crazy_gadget_2 = "Crazy Gadget - 2" +crazy_gadget_3 = "Crazy Gadget - 3" +crazy_gadget_4 = "Crazy Gadget - 4" +crazy_gadget_5 = "Crazy Gadget - 5" +crazy_gadget_chao_1 = "Crazy Gadget - Chao Key 1" +crazy_gadget_chao_2 = "Crazy Gadget - Chao Key 2" +crazy_gadget_chao_3 = "Crazy Gadget - Chao Key 3" +crazy_gadget_pipe_1 = "Crazy Gadget - Pipe 1" +crazy_gadget_pipe_2 = "Crazy Gadget - Pipe 2" +crazy_gadget_pipe_3 = "Crazy Gadget - Pipe 3" +crazy_gadget_pipe_4 = "Crazy Gadget - Pipe 4" +crazy_gadget_hidden_1 = "Crazy Gadget - Hidden 1" +crazy_gadget_beetle = "Crazy Gadget - Gold Beetle" +crazy_gadget_upgrade = "Crazy Gadget - Upgrade" +final_rush_1 = "Final Rush - 1" +final_rush_2 = "Final Rush - 2" +final_rush_3 = "Final Rush - 3" +final_rush_4 = "Final Rush - 4" +final_rush_5 = "Final Rush - 5" +final_rush_chao_1 = "Final Rush - Chao Key 1" +final_rush_chao_2 = "Final Rush - Chao Key 2" +final_rush_chao_3 = "Final Rush - Chao Key 3" +final_rush_pipe_1 = "Final Rush - Pipe 1" +final_rush_pipe_2 = "Final Rush - Pipe 2" +final_rush_beetle = "Final Rush - Gold Beetle" +final_rush_upgrade = "Final Rush - Upgrade" # Tails Mission Definitions -prison_lane_1 = "Prison Lane - 1" -prison_lane_2 = "Prison Lane - 2" -prison_lane_3 = "Prison Lane - 3" -prison_lane_4 = "Prison Lane - 4" -prison_lane_5 = "Prison Lane - 5" -prison_lane_upgrade = "Prison Lane - Upgrade" -mission_street_1 = "Mission Street - 1" -mission_street_2 = "Mission Street - 2" -mission_street_3 = "Mission Street - 3" -mission_street_4 = "Mission Street - 4" -mission_street_5 = "Mission Street - 5" -mission_street_upgrade = "Mission Street - Upgrade" -route_101_1 = "Route 101 - 1" -route_101_2 = "Route 101 - 2" -route_101_3 = "Route 101 - 3" -route_101_4 = "Route 101 - 4" -route_101_5 = "Route 101 - 5" -hidden_base_1 = "Hidden Base - 1" -hidden_base_2 = "Hidden Base - 2" -hidden_base_3 = "Hidden Base - 3" -hidden_base_4 = "Hidden Base - 4" -hidden_base_5 = "Hidden Base - 5" -hidden_base_upgrade = "Hidden Base - Upgrade" -eternal_engine_1 = "Eternal Engine - 1" -eternal_engine_2 = "Eternal Engine - 2" -eternal_engine_3 = "Eternal Engine - 3" -eternal_engine_4 = "Eternal Engine - 4" -eternal_engine_5 = "Eternal Engine - 5" -eternal_engine_upgrade = "Eternal Engine - Upgrade" +prison_lane_1 = "Prison Lane - 1" +prison_lane_2 = "Prison Lane - 2" +prison_lane_3 = "Prison Lane - 3" +prison_lane_4 = "Prison Lane - 4" +prison_lane_5 = "Prison Lane - 5" +prison_lane_chao_1 = "Prison Lane - Chao Key 1" +prison_lane_chao_2 = "Prison Lane - Chao Key 2" +prison_lane_chao_3 = "Prison Lane - Chao Key 3" +prison_lane_pipe_1 = "Prison Lane - Pipe 1" +prison_lane_pipe_2 = "Prison Lane - Pipe 2" +prison_lane_pipe_3 = "Prison Lane - Pipe 3" +prison_lane_hidden_1 = "Prison Lane - Hidden 1" +prison_lane_hidden_2 = "Prison Lane - Hidden 2" +prison_lane_hidden_3 = "Prison Lane - Hidden 3" +prison_lane_beetle = "Prison Lane - Gold Beetle" +prison_lane_upgrade = "Prison Lane - Upgrade" +mission_street_1 = "Mission Street - 1" +mission_street_2 = "Mission Street - 2" +mission_street_3 = "Mission Street - 3" +mission_street_4 = "Mission Street - 4" +mission_street_5 = "Mission Street - 5" +mission_street_chao_1 = "Mission Street - Chao Key 1" +mission_street_chao_2 = "Mission Street - Chao Key 2" +mission_street_chao_3 = "Mission Street - Chao Key 3" +mission_street_pipe_1 = "Mission Street - Pipe 1" +mission_street_pipe_2 = "Mission Street - Pipe 2" +mission_street_pipe_3 = "Mission Street - Pipe 3" +mission_street_hidden_1 = "Mission Street - Hidden 1" +mission_street_hidden_2 = "Mission Street - Hidden 2" +mission_street_hidden_3 = "Mission Street - Hidden 3" +mission_street_beetle = "Mission Street - Gold Beetle" +mission_street_upgrade = "Mission Street - Upgrade" +route_101_1 = "Route 101 - 1" +route_101_2 = "Route 101 - 2" +route_101_3 = "Route 101 - 3" +route_101_4 = "Route 101 - 4" +route_101_5 = "Route 101 - 5" +hidden_base_1 = "Hidden Base - 1" +hidden_base_2 = "Hidden Base - 2" +hidden_base_3 = "Hidden Base - 3" +hidden_base_4 = "Hidden Base - 4" +hidden_base_5 = "Hidden Base - 5" +hidden_base_chao_1 = "Hidden Base - Chao Key 1" +hidden_base_chao_2 = "Hidden Base - Chao Key 2" +hidden_base_pipe_1 = "Hidden Base - Pipe 1" +hidden_base_pipe_2 = "Hidden Base - Pipe 2" +hidden_base_pipe_3 = "Hidden Base - Pipe 3" +hidden_base_pipe_4 = "Hidden Base - Pipe 4" +hidden_base_pipe_5 = "Hidden Base - Pipe 5" +hidden_base_beetle = "Hidden Base - Gold Beetle" +hidden_base_upgrade = "Hidden Base - Upgrade" +eternal_engine_1 = "Eternal Engine - 1" +eternal_engine_2 = "Eternal Engine - 2" +eternal_engine_3 = "Eternal Engine - 3" +eternal_engine_4 = "Eternal Engine - 4" +eternal_engine_5 = "Eternal Engine - 5" +eternal_engine_chao_1 = "Eternal Engine - Chao Key 1" +eternal_engine_chao_2 = "Eternal Engine - Chao Key 2" +eternal_engine_chao_3 = "Eternal Engine - Chao Key 3" +eternal_engine_pipe_1 = "Eternal Engine - Pipe 1" +eternal_engine_pipe_2 = "Eternal Engine - Pipe 2" +eternal_engine_pipe_3 = "Eternal Engine - Pipe 3" +eternal_engine_pipe_4 = "Eternal Engine - Pipe 4" +eternal_engine_pipe_5 = "Eternal Engine - Pipe 5" +eternal_engine_beetle = "Eternal Engine - Gold Beetle" +eternal_engine_upgrade = "Eternal Engine - Upgrade" # Knuckles Mission Definitions -wild_canyon_1 = "Wild Canyon - 1" -wild_canyon_2 = "Wild Canyon - 2" -wild_canyon_3 = "Wild Canyon - 3" -wild_canyon_4 = "Wild Canyon - 4" -wild_canyon_5 = "Wild Canyon - 5" -wild_canyon_upgrade = "Wild Canyon - Upgrade" -pumpkin_hill_1 = "Pumpkin Hill - 1" -pumpkin_hill_2 = "Pumpkin Hill - 2" -pumpkin_hill_3 = "Pumpkin Hill - 3" -pumpkin_hill_4 = "Pumpkin Hill - 4" -pumpkin_hill_5 = "Pumpkin Hill - 5" -pumpkin_hill_upgrade = "Pumpkin Hill - Upgrade" -aquatic_mine_1 = "Aquatic Mine - 1" -aquatic_mine_2 = "Aquatic Mine - 2" -aquatic_mine_3 = "Aquatic Mine - 3" -aquatic_mine_4 = "Aquatic Mine - 4" -aquatic_mine_5 = "Aquatic Mine - 5" -aquatic_mine_upgrade = "Aquatic Mine - Upgrade" -death_chamber_1 = "Death Chamber - 1" -death_chamber_2 = "Death Chamber - 2" -death_chamber_3 = "Death Chamber - 3" -death_chamber_4 = "Death Chamber - 4" -death_chamber_5 = "Death Chamber - 5" -death_chamber_upgrade = "Death Chamber - Upgrade" -meteor_herd_1 = "Meteor Herd - 1" -meteor_herd_2 = "Meteor Herd - 2" -meteor_herd_3 = "Meteor Herd - 3" -meteor_herd_4 = "Meteor Herd - 4" -meteor_herd_5 = "Meteor Herd - 5" -meteor_herd_upgrade = "Meteor Herd - Upgrade" +wild_canyon_1 = "Wild Canyon - 1" +wild_canyon_2 = "Wild Canyon - 2" +wild_canyon_3 = "Wild Canyon - 3" +wild_canyon_4 = "Wild Canyon - 4" +wild_canyon_5 = "Wild Canyon - 5" +wild_canyon_chao_1 = "Wild Canyon - Chao Key 1" +wild_canyon_chao_2 = "Wild Canyon - Chao Key 2" +wild_canyon_chao_3 = "Wild Canyon - Chao Key 3" +wild_canyon_pipe_1 = "Wild Canyon - Pipe 1" +wild_canyon_pipe_2 = "Wild Canyon - Pipe 2" +wild_canyon_pipe_3 = "Wild Canyon - Pipe 3" +wild_canyon_beetle = "Wild Canyon - Gold Beetle" +wild_canyon_upgrade = "Wild Canyon - Upgrade" +pumpkin_hill_1 = "Pumpkin Hill - 1" +pumpkin_hill_2 = "Pumpkin Hill - 2" +pumpkin_hill_3 = "Pumpkin Hill - 3" +pumpkin_hill_4 = "Pumpkin Hill - 4" +pumpkin_hill_5 = "Pumpkin Hill - 5" +pumpkin_hill_chao_1 = "Pumpkin Hill - Chao Key 1" +pumpkin_hill_chao_2 = "Pumpkin Hill - Chao Key 2" +pumpkin_hill_chao_3 = "Pumpkin Hill - Chao Key 3" +pumpkin_hill_pipe_1 = "Pumpkin Hill - Pipe 1" +pumpkin_hill_hidden_1 = "Pumpkin Hill - Hidden 1" +pumpkin_hill_upgrade = "Pumpkin Hill - Upgrade" +aquatic_mine_1 = "Aquatic Mine - 1" +aquatic_mine_2 = "Aquatic Mine - 2" +aquatic_mine_3 = "Aquatic Mine - 3" +aquatic_mine_4 = "Aquatic Mine - 4" +aquatic_mine_5 = "Aquatic Mine - 5" +aquatic_mine_chao_1 = "Aquatic Mine - Chao Key 1" +aquatic_mine_chao_2 = "Aquatic Mine - Chao Key 2" +aquatic_mine_chao_3 = "Aquatic Mine - Chao Key 3" +aquatic_mine_pipe_1 = "Aquatic Mine - Pipe 1" +aquatic_mine_pipe_2 = "Aquatic Mine - Pipe 2" +aquatic_mine_pipe_3 = "Aquatic Mine - Pipe 3" +aquatic_mine_beetle = "Aquatic Mine - Gold Beetle" +aquatic_mine_upgrade = "Aquatic Mine - Upgrade" +death_chamber_1 = "Death Chamber - 1" +death_chamber_2 = "Death Chamber - 2" +death_chamber_3 = "Death Chamber - 3" +death_chamber_4 = "Death Chamber - 4" +death_chamber_5 = "Death Chamber - 5" +death_chamber_chao_1 = "Death Chamber - Chao Key 1" +death_chamber_chao_2 = "Death Chamber - Chao Key 2" +death_chamber_chao_3 = "Death Chamber - Chao Key 3" +death_chamber_pipe_1 = "Death Chamber - Pipe 1" +death_chamber_pipe_2 = "Death Chamber - Pipe 2" +death_chamber_pipe_3 = "Death Chamber - Pipe 3" +death_chamber_hidden_1 = "Death Chamber - Hidden 1" +death_chamber_hidden_2 = "Death Chamber - Hidden 2" +death_chamber_beetle = "Death Chamber - Gold Beetle" +death_chamber_upgrade = "Death Chamber - Upgrade" +meteor_herd_1 = "Meteor Herd - 1" +meteor_herd_2 = "Meteor Herd - 2" +meteor_herd_3 = "Meteor Herd - 3" +meteor_herd_4 = "Meteor Herd - 4" +meteor_herd_5 = "Meteor Herd - 5" +meteor_herd_chao_1 = "Meteor Herd - Chao Key 1" +meteor_herd_chao_2 = "Meteor Herd - Chao Key 2" +meteor_herd_chao_3 = "Meteor Herd - Chao Key 3" +meteor_herd_pipe_1 = "Meteor Herd - Pipe 1" +meteor_herd_pipe_2 = "Meteor Herd - Pipe 2" +meteor_herd_pipe_3 = "Meteor Herd - Pipe 3" +meteor_herd_beetle = "Meteor Herd - Gold Beetle" +meteor_herd_upgrade = "Meteor Herd - Upgrade" # Shadow Mission Definitions -radical_highway_1 = "Radical Highway - 1" -radical_highway_2 = "Radical Highway - 2" -radical_highway_3 = "Radical Highway - 3" -radical_highway_4 = "Radical Highway - 4" -radical_highway_5 = "Radical Highway - 5" -radical_highway_upgrade = "Radical Highway - Upgrade" -white_jungle_1 = "White Jungle - 1" -white_jungle_2 = "White Jungle - 2" -white_jungle_3 = "White Jungle - 3" -white_jungle_4 = "White Jungle - 4" -white_jungle_5 = "White Jungle - 5" -white_jungle_upgrade = "White Jungle - Upgrade" -sky_rail_1 = "Sky Rail - 1" -sky_rail_2 = "Sky Rail - 2" -sky_rail_3 = "Sky Rail - 3" -sky_rail_4 = "Sky Rail - 4" -sky_rail_5 = "Sky Rail - 5" -sky_rail_upgrade = "Sky Rail - Upgrade" -final_chase_1 = "Final Chase - 1" -final_chase_2 = "Final Chase - 2" -final_chase_3 = "Final Chase - 3" -final_chase_4 = "Final Chase - 4" -final_chase_5 = "Final Chase - 5" -final_chase_upgrade = "Final Chase - Upgrade" +radical_highway_1 = "Radical Highway - 1" +radical_highway_2 = "Radical Highway - 2" +radical_highway_3 = "Radical Highway - 3" +radical_highway_4 = "Radical Highway - 4" +radical_highway_5 = "Radical Highway - 5" +radical_highway_chao_1 = "Radical Highway - Chao Key 1" +radical_highway_chao_2 = "Radical Highway - Chao Key 2" +radical_highway_chao_3 = "Radical Highway - Chao Key 3" +radical_highway_pipe_1 = "Radical Highway - Pipe 1" +radical_highway_pipe_2 = "Radical Highway - Pipe 2" +radical_highway_pipe_3 = "Radical Highway - Pipe 3" +radical_highway_hidden_1 = "Radical Highway - Hidden 1" +radical_highway_hidden_2 = "Radical Highway - Hidden 2" +radical_highway_hidden_3 = "Radical Highway - Hidden 3" +radical_highway_beetle = "Radical Highway - Gold Beetle" +radical_highway_upgrade = "Radical Highway - Upgrade" +white_jungle_1 = "White Jungle - 1" +white_jungle_2 = "White Jungle - 2" +white_jungle_3 = "White Jungle - 3" +white_jungle_4 = "White Jungle - 4" +white_jungle_5 = "White Jungle - 5" +white_jungle_chao_1 = "White Jungle - Chao Key 1" +white_jungle_chao_2 = "White Jungle - Chao Key 2" +white_jungle_chao_3 = "White Jungle - Chao Key 3" +white_jungle_pipe_1 = "White Jungle - Pipe 1" +white_jungle_pipe_2 = "White Jungle - Pipe 2" +white_jungle_pipe_3 = "White Jungle - Pipe 3" +white_jungle_pipe_4 = "White Jungle - Pipe 4" +white_jungle_hidden_1 = "White Jungle - Hidden 1" +white_jungle_hidden_2 = "White Jungle - Hidden 2" +white_jungle_hidden_3 = "White Jungle - Hidden 3" +white_jungle_beetle = "White Jungle - Gold Beetle" +white_jungle_upgrade = "White Jungle - Upgrade" +sky_rail_1 = "Sky Rail - 1" +sky_rail_2 = "Sky Rail - 2" +sky_rail_3 = "Sky Rail - 3" +sky_rail_4 = "Sky Rail - 4" +sky_rail_5 = "Sky Rail - 5" +sky_rail_chao_1 = "Sky Rail - Chao Key 1" +sky_rail_chao_2 = "Sky Rail - Chao Key 2" +sky_rail_chao_3 = "Sky Rail - Chao Key 3" +sky_rail_pipe_1 = "Sky Rail - Pipe 1" +sky_rail_pipe_2 = "Sky Rail - Pipe 2" +sky_rail_pipe_3 = "Sky Rail - Pipe 3" +sky_rail_pipe_4 = "Sky Rail - Pipe 4" +sky_rail_pipe_5 = "Sky Rail - Pipe 5" +sky_rail_pipe_6 = "Sky Rail - Pipe 6" +sky_rail_beetle = "Sky Rail - Gold Beetle" +sky_rail_upgrade = "Sky Rail - Upgrade" +final_chase_1 = "Final Chase - 1" +final_chase_2 = "Final Chase - 2" +final_chase_3 = "Final Chase - 3" +final_chase_4 = "Final Chase - 4" +final_chase_5 = "Final Chase - 5" +final_chase_chao_1 = "Final Chase - Chao Key 1" +final_chase_chao_2 = "Final Chase - Chao Key 2" +final_chase_chao_3 = "Final Chase - Chao Key 3" +final_chase_pipe_1 = "Final Chase - Pipe 1" +final_chase_pipe_2 = "Final Chase - Pipe 2" +final_chase_pipe_3 = "Final Chase - Pipe 3" +final_chase_beetle = "Final Chase - Gold Beetle" +final_chase_upgrade = "Final Chase - Upgrade" # Eggman Mission Definitions -iron_gate_1 = "Iron Gate - 1" -iron_gate_2 = "Iron Gate - 2" -iron_gate_3 = "Iron Gate - 3" -iron_gate_4 = "Iron Gate - 4" -iron_gate_5 = "Iron Gate - 5" -iron_gate_upgrade = "Iron Gate - Upgrade" -sand_ocean_1 = "Sand Ocean - 1" -sand_ocean_2 = "Sand Ocean - 2" -sand_ocean_3 = "Sand Ocean - 3" -sand_ocean_4 = "Sand Ocean - 4" -sand_ocean_5 = "Sand Ocean - 5" -sand_ocean_upgrade = "Sand Ocean - Upgrade" -lost_colony_1 = "Lost Colony - 1" -lost_colony_2 = "Lost Colony - 2" -lost_colony_3 = "Lost Colony - 3" -lost_colony_4 = "Lost Colony - 4" -lost_colony_5 = "Lost Colony - 5" -lost_colony_upgrade = "Lost Colony - Upgrade" -weapons_bed_1 = "Weapons Bed - 1" -weapons_bed_2 = "Weapons Bed - 2" -weapons_bed_3 = "Weapons Bed - 3" -weapons_bed_4 = "Weapons Bed - 4" -weapons_bed_5 = "Weapons Bed - 5" -weapons_bed_upgrade = "Weapons Bed - Upgrade" -cosmic_wall_1 = "Cosmic Wall - 1" -cosmic_wall_2 = "Cosmic Wall - 2" -cosmic_wall_3 = "Cosmic Wall - 3" -cosmic_wall_4 = "Cosmic Wall - 4" -cosmic_wall_5 = "Cosmic Wall - 5" -cosmic_wall_upgrade = "Cosmic Wall - Upgrade" +iron_gate_1 = "Iron Gate - 1" +iron_gate_2 = "Iron Gate - 2" +iron_gate_3 = "Iron Gate - 3" +iron_gate_4 = "Iron Gate - 4" +iron_gate_5 = "Iron Gate - 5" +iron_gate_chao_1 = "Iron Gate - Chao Key 1" +iron_gate_chao_2 = "Iron Gate - Chao Key 2" +iron_gate_chao_3 = "Iron Gate - Chao Key 3" +iron_gate_pipe_1 = "Iron Gate - Pipe 1" +iron_gate_pipe_2 = "Iron Gate - Pipe 2" +iron_gate_pipe_3 = "Iron Gate - Pipe 3" +iron_gate_pipe_4 = "Iron Gate - Pipe 4" +iron_gate_pipe_5 = "Iron Gate - Pipe 5" +iron_gate_beetle = "Iron Gate - Gold Beetle" +iron_gate_upgrade = "Iron Gate - Upgrade" +sand_ocean_1 = "Sand Ocean - 1" +sand_ocean_2 = "Sand Ocean - 2" +sand_ocean_3 = "Sand Ocean - 3" +sand_ocean_4 = "Sand Ocean - 4" +sand_ocean_5 = "Sand Ocean - 5" +sand_ocean_chao_1 = "Sand Ocean - Chao Key 1" +sand_ocean_chao_2 = "Sand Ocean - Chao Key 2" +sand_ocean_chao_3 = "Sand Ocean - Chao Key 3" +sand_ocean_pipe_1 = "Sand Ocean - Pipe 1" +sand_ocean_pipe_2 = "Sand Ocean - Pipe 2" +sand_ocean_pipe_3 = "Sand Ocean - Pipe 3" +sand_ocean_pipe_4 = "Sand Ocean - Pipe 4" +sand_ocean_pipe_5 = "Sand Ocean - Pipe 5" +sand_ocean_beetle = "Sand Ocean - Gold Beetle" +sand_ocean_upgrade = "Sand Ocean - Upgrade" +lost_colony_1 = "Lost Colony - 1" +lost_colony_2 = "Lost Colony - 2" +lost_colony_3 = "Lost Colony - 3" +lost_colony_4 = "Lost Colony - 4" +lost_colony_5 = "Lost Colony - 5" +lost_colony_chao_1 = "Lost Colony - Chao Key 1" +lost_colony_chao_2 = "Lost Colony - Chao Key 2" +lost_colony_chao_3 = "Lost Colony - Chao Key 3" +lost_colony_pipe_1 = "Lost Colony - Pipe 1" +lost_colony_pipe_2 = "Lost Colony - Pipe 2" +lost_colony_hidden_1 = "Lost Colony - Hidden 1" +lost_colony_beetle = "Lost Colony - Gold Beetle" +lost_colony_upgrade = "Lost Colony - Upgrade" +weapons_bed_1 = "Weapons Bed - 1" +weapons_bed_2 = "Weapons Bed - 2" +weapons_bed_3 = "Weapons Bed - 3" +weapons_bed_4 = "Weapons Bed - 4" +weapons_bed_5 = "Weapons Bed - 5" +weapons_bed_chao_1 = "Weapons Bed - Chao Key 1" +weapons_bed_chao_2 = "Weapons Bed - Chao Key 2" +weapons_bed_chao_3 = "Weapons Bed - Chao Key 3" +weapons_bed_pipe_1 = "Weapons Bed - Pipe 1" +weapons_bed_pipe_2 = "Weapons Bed - Pipe 2" +weapons_bed_pipe_3 = "Weapons Bed - Pipe 3" +weapons_bed_pipe_4 = "Weapons Bed - Pipe 4" +weapons_bed_pipe_5 = "Weapons Bed - Pipe 5" +weapons_bed_upgrade = "Weapons Bed - Upgrade" +cosmic_wall_1 = "Cosmic Wall - 1" +cosmic_wall_2 = "Cosmic Wall - 2" +cosmic_wall_3 = "Cosmic Wall - 3" +cosmic_wall_4 = "Cosmic Wall - 4" +cosmic_wall_5 = "Cosmic Wall - 5" +cosmic_wall_chao_1 = "Cosmic Wall - Chao Key 1" +cosmic_wall_chao_2 = "Cosmic Wall - Chao Key 2" +cosmic_wall_chao_3 = "Cosmic Wall - Chao Key 3" +cosmic_wall_pipe_1 = "Cosmic Wall - Pipe 1" +cosmic_wall_pipe_2 = "Cosmic Wall - Pipe 2" +cosmic_wall_pipe_3 = "Cosmic Wall - Pipe 3" +cosmic_wall_pipe_4 = "Cosmic Wall - Pipe 4" +cosmic_wall_pipe_5 = "Cosmic Wall - Pipe 5" +cosmic_wall_beetle = "Cosmic Wall - Gold Beetle" +cosmic_wall_upgrade = "Cosmic Wall - Upgrade" # Rouge Mission Definitions -dry_lagoon_1 = "Dry Lagoon - 1" -dry_lagoon_2 = "Dry Lagoon - 2" -dry_lagoon_3 = "Dry Lagoon - 3" -dry_lagoon_4 = "Dry Lagoon - 4" -dry_lagoon_5 = "Dry Lagoon - 5" -dry_lagoon_upgrade = "Dry Lagoon - Upgrade" -egg_quarters_1 = "Egg Quarters - 1" -egg_quarters_2 = "Egg Quarters - 2" -egg_quarters_3 = "Egg Quarters - 3" -egg_quarters_4 = "Egg Quarters - 4" -egg_quarters_5 = "Egg Quarters - 5" -egg_quarters_upgrade = "Egg Quarters - Upgrade" -security_hall_1 = "Security Hall - 1" -security_hall_2 = "Security Hall - 2" -security_hall_3 = "Security Hall - 3" -security_hall_4 = "Security Hall - 4" -security_hall_5 = "Security Hall - 5" -security_hall_upgrade = "Security Hall - Upgrade" -route_280_1 = "Route 280 - 1" -route_280_2 = "Route 280 - 2" -route_280_3 = "Route 280 - 3" -route_280_4 = "Route 280 - 4" -route_280_5 = "Route 280 - 5" -mad_space_1 = "Mad Space - 1" -mad_space_2 = "Mad Space - 2" -mad_space_3 = "Mad Space - 3" -mad_space_4 = "Mad Space - 4" -mad_space_5 = "Mad Space - 5" -mad_space_upgrade = "Mad Space - Upgrade" +dry_lagoon_1 = "Dry Lagoon - 1" +dry_lagoon_2 = "Dry Lagoon - 2" +dry_lagoon_3 = "Dry Lagoon - 3" +dry_lagoon_4 = "Dry Lagoon - 4" +dry_lagoon_5 = "Dry Lagoon - 5" +dry_lagoon_chao_1 = "Dry Lagoon - Chao Key 1" +dry_lagoon_chao_2 = "Dry Lagoon - Chao Key 2" +dry_lagoon_chao_3 = "Dry Lagoon - Chao Key 3" +dry_lagoon_pipe_1 = "Dry Lagoon - Pipe 1" +dry_lagoon_hidden_1 = "Dry Lagoon - Hidden 1" +dry_lagoon_beetle = "Dry Lagoon - Gold Beetle" +dry_lagoon_upgrade = "Dry Lagoon - Upgrade" +egg_quarters_1 = "Egg Quarters - 1" +egg_quarters_2 = "Egg Quarters - 2" +egg_quarters_3 = "Egg Quarters - 3" +egg_quarters_4 = "Egg Quarters - 4" +egg_quarters_5 = "Egg Quarters - 5" +egg_quarters_chao_1 = "Egg Quarters - Chao Key 1" +egg_quarters_chao_2 = "Egg Quarters - Chao Key 2" +egg_quarters_chao_3 = "Egg Quarters - Chao Key 3" +egg_quarters_pipe_1 = "Egg Quarters - Pipe 1" +egg_quarters_pipe_2 = "Egg Quarters - Pipe 2" +egg_quarters_hidden_1 = "Egg Quarters - Hidden 1" +egg_quarters_hidden_2 = "Egg Quarters - Hidden 2" +egg_quarters_beetle = "Egg Quarters - Gold Beetle" +egg_quarters_upgrade = "Egg Quarters - Upgrade" +security_hall_1 = "Security Hall - 1" +security_hall_2 = "Security Hall - 2" +security_hall_3 = "Security Hall - 3" +security_hall_4 = "Security Hall - 4" +security_hall_5 = "Security Hall - 5" +security_hall_chao_1 = "Security Hall - Chao Key 1" +security_hall_chao_2 = "Security Hall - Chao Key 2" +security_hall_chao_3 = "Security Hall - Chao Key 3" +security_hall_pipe_1 = "Security Hall - Pipe 1" +security_hall_hidden_1 = "Security Hall - Hidden 1" +security_hall_beetle = "Security Hall - Gold Beetle" +security_hall_upgrade = "Security Hall - Upgrade" +route_280_1 = "Route 280 - 1" +route_280_2 = "Route 280 - 2" +route_280_3 = "Route 280 - 3" +route_280_4 = "Route 280 - 4" +route_280_5 = "Route 280 - 5" +mad_space_1 = "Mad Space - 1" +mad_space_2 = "Mad Space - 2" +mad_space_3 = "Mad Space - 3" +mad_space_4 = "Mad Space - 4" +mad_space_5 = "Mad Space - 5" +mad_space_chao_1 = "Mad Space - Chao Key 1" +mad_space_chao_2 = "Mad Space - Chao Key 2" +mad_space_chao_3 = "Mad Space - Chao Key 3" +mad_space_pipe_1 = "Mad Space - Pipe 1" +mad_space_pipe_2 = "Mad Space - Pipe 2" +mad_space_pipe_3 = "Mad Space - Pipe 3" +mad_space_pipe_4 = "Mad Space - Pipe 4" +mad_space_beetle = "Mad Space - Gold Beetle" +mad_space_upgrade = "Mad Space - Upgrade" # Final Mission Definitions -cannon_core_1 = "Cannon Core - 1" -cannon_core_2 = "Cannon Core - 2" -cannon_core_3 = "Cannon Core - 3" -cannon_core_4 = "Cannon Core - 4" -cannon_core_5 = "Cannon Core - 5" +cannon_core_1 = "Cannon Core - 1" +cannon_core_2 = "Cannon Core - 2" +cannon_core_3 = "Cannon Core - 3" +cannon_core_4 = "Cannon Core - 4" +cannon_core_5 = "Cannon Core - 5" +cannon_core_chao_1 = "Cannon Core - Chao Key 1" +cannon_core_chao_2 = "Cannon Core - Chao Key 2" +cannon_core_chao_3 = "Cannon Core - Chao Key 3" +cannon_core_pipe_1 = "Cannon Core - Pipe 1" +cannon_core_pipe_2 = "Cannon Core - Pipe 2" +cannon_core_pipe_3 = "Cannon Core - Pipe 3" +cannon_core_pipe_4 = "Cannon Core - Pipe 4" +cannon_core_pipe_5 = "Cannon Core - Pipe 5" +cannon_core_hidden_1 = "Cannon Core - Hidden 1" +cannon_core_beetle = "Cannon Core - Gold Beetle" # Boss Definitions gate_1_boss = "Gate 1 Boss" @@ -277,8 +518,10 @@ chao_super_karate = "Chao Karate - Super" # Other Definitions -green_hill = "Green Hill" -biolizard = "Biolizard" +green_hill = "Green Hill" +green_hill_chao_1 = "Green Hill - Chao Key 1" +biolizard = "Biolizard" +finalhazard = "Finalhazard" # Region Definitions @@ -336,6 +579,8 @@ biolizard_region = "Biolizard" +green_hill_region = "Green Hill" + chao_garden_beginner_region = "Chao Garden - Beginner" chao_garden_intermediate_region = "Chao Garden - Intermediate" chao_garden_expert_region = "Chao Garden - Expert" diff --git a/worlds/sa2b/Options.py b/worlds/sa2b/Options.py index 6010229ea5c9..4724d30a5f22 100644 --- a/worlds/sa2b/Options.py +++ b/worlds/sa2b/Options.py @@ -3,6 +3,27 @@ from Options import Choice, Range, Option, Toggle, DeathLink, DefaultOnToggle, OptionList +class Goal(Choice): + """ + Determines the goal of the seed + Biolizard: Finish Cannon's Core and defeat the Biolizard and Finalhazard + Chaos Emerald Hunt: Find the Seven Chaos Emeralds and reach Green Hill Zone + Finalhazard Chaos Emerald Hunt: Find the Seven Chaos Emeralds and reach Green Hill Zone, then defeat Finalhazard + """ + display_name = "Goal" + option_biolizard = 0 + option_chaos_emerald_hunt = 1 + option_finalhazard_chaos_emerald_hunt = 2 + default = 0 + + +class MissionShuffle(Toggle): + """ + Determines whether missions order will be shuffled per level + """ + display_name = "Mission Shuffle" + + class BaseTrapWeight(Choice): """ Base Class for Trap Weights @@ -42,6 +63,27 @@ class TinyTrapWeight(BaseTrapWeight): display_name = "Tiny Trap Weight" +class GravityTrapWeight(BaseTrapWeight): + """ + Likelihood of a receiving a trap which increases gravity + """ + display_name = "Gravity Trap Weight" + + +class ExpositionTrapWeight(BaseTrapWeight): + """ + Likelihood of a receiving a trap which tells you the story + """ + display_name = "Exposition Trap Weight" + + +class DarknessTrapWeight(BaseTrapWeight): + """ + Likelihood of a receiving a trap which makes the world dark + """ + display_name = "Darkness Trap Weight" + + class JunkFillPercentage(Range): """ Replace a percentage of non-required emblems in the item pool with random junk items @@ -78,11 +120,41 @@ class IncludeMissions(Range): default = 2 +class Keysanity(Toggle): + """ + Determines whether picking up Chao Keys grants checks + """ + display_name = "Keysanity" + + +class Whistlesanity(Choice): + """ + Determines whether whistling at various spots grants checks + None: No Whistle Spots grant checks + Pipes: Whistling at Pipes grants checks + Hidden: Whistling at Hidden Whistle Spots grants checks + Both: Whistling at both Pipes and Hidden Whistle Spots grants checks + """ + display_name = "Whistlesanity" + option_none = 0 + option_pipes = 1 + option_hidden = 2 + option_both = 3 + default = 0 + + +class Beetlesanity(Toggle): + """ + Determines whether destroying Gold Beetles grants checks + """ + display_name = "Beetlesanity" + + class EmblemPercentageForCannonsCore(Range): """ Allows logic to gate the final mission behind a number of Emblems """ - display_name = "Emblem Percentage for Cannons Core" + display_name = "Emblem Percentage for Cannon's Core" range_start = 0 range_end = 75 default = 50 @@ -172,24 +244,280 @@ class ChaoRaceChecks(Choice): default = 0 +class RequiredCannonsCoreMissions(Choice): + """ + Determines how many Cannon's Core missions must be completed to unlock the Biolizard (for the "Biolizard" goal) + First: Only the first mission must be completed + All Active: All active Cannon's Core missions must be completed + """ + display_name = "Required Cannon's Core Missions" + option_first = 0 + option_all_active = 1 + default = 0 + + +class BaseMissionCount(Range): + """ + Base class for mission count options + """ + range_start = 1 + range_end = 5 + default = 2 + + +class SpeedMissionCount(BaseMissionCount): + """ + The number of active missions to include for Sonic and Shadow stages + """ + display_name = "Speed Mission Count" + + +class SpeedMission2(DefaultOnToggle): + """ + Determines if the Sonic and Shadow 100 rings missions should be included + """ + display_name = "Speed Mission 2" + + +class SpeedMission3(DefaultOnToggle): + """ + Determines if the Sonic and Shadow lost chao missions should be included + """ + display_name = "Speed Mission 3" + + +class SpeedMission4(DefaultOnToggle): + """ + Determines if the Sonic and Shadow time trial missions should be included + """ + display_name = "Speed Mission 4" + + +class SpeedMission5(DefaultOnToggle): + """ + Determines if the Sonic and Shadow hard missions should be included + """ + display_name = "Speed Mission 5" + + +class MechMissionCount(BaseMissionCount): + """ + The number of active missions to include for Tails and Eggman stages + """ + display_name = "Mech Mission Count" + + +class MechMission2(DefaultOnToggle): + """ + Determines if the Tails and Eggman 100 rings missions should be included + """ + display_name = "Mech Mission 2" + + +class MechMission3(DefaultOnToggle): + """ + Determines if the Tails and Eggman lost chao missions should be included + """ + display_name = "Mech Mission 3" + + +class MechMission4(DefaultOnToggle): + """ + Determines if the Tails and Eggman time trial missions should be included + """ + display_name = "Mech Mission 4" + + +class MechMission5(DefaultOnToggle): + """ + Determines if the Tails and Eggman hard missions should be included + """ + display_name = "Mech Mission 5" + + +class HuntMissionCount(BaseMissionCount): + """ + The number of active missions to include for Knuckles and Rouge stages + """ + display_name = "Hunt Mission Count" + + +class HuntMission2(DefaultOnToggle): + """ + Determines if the Knuckles and Rouge 100 rings missions should be included + """ + display_name = "Hunt Mission 2" + + +class HuntMission3(DefaultOnToggle): + """ + Determines if the Knuckles and Rouge lost chao missions should be included + """ + display_name = "Hunt Mission 3" + + +class HuntMission4(DefaultOnToggle): + """ + Determines if the Knuckles and Rouge time trial missions should be included + """ + display_name = "Hunt Mission 4" + + +class HuntMission5(DefaultOnToggle): + """ + Determines if the Knuckles and Rouge hard missions should be included + """ + display_name = "Hunt Mission 5" + + +class KartMissionCount(BaseMissionCount): + """ + The number of active missions to include for Route 101 and 280 + """ + display_name = "Kart Mission Count" + + +class KartMission2(DefaultOnToggle): + """ + Determines if the Route 101 and 280 100 rings missions should be included + """ + display_name = "Kart Mission 2" + + +class KartMission3(DefaultOnToggle): + """ + Determines if the Route 101 and 280 avoid cars missions should be included + """ + display_name = "Kart Mission 3" + + +class KartMission4(DefaultOnToggle): + """ + Determines if the Route 101 and 280 avoid walls missions should be included + """ + display_name = "Kart Mission 4" + + +class KartMission5(DefaultOnToggle): + """ + Determines if the Route 101 and 280 hard missions should be included + """ + display_name = "Kart Mission 5" + + +class CannonsCoreMissionCount(BaseMissionCount): + """ + The number of active missions to include for Cannon's Core + """ + display_name = "Cannon's Core Mission Count" + + +class CannonsCoreMission2(DefaultOnToggle): + """ + Determines if the Cannon's Core 100 rings mission should be included + """ + display_name = "Cannon's Core Mission 2" + + +class CannonsCoreMission3(DefaultOnToggle): + """ + Determines if the Cannon's Core lost chao mission should be included + """ + display_name = "Cannon's Core Mission 3" + + +class CannonsCoreMission4(DefaultOnToggle): + """ + Determines if the Cannon's Core time trial mission should be included + """ + display_name = "Cannon's Core Mission 4" + + +class CannonsCoreMission5(DefaultOnToggle): + """ + Determines if the Cannon's Core hard mission should be included + """ + display_name = "Cannon's Core Mission 5" + + +class SADXMusic(Choice): + """ + Whether the randomizer will include Sonic Adventure DX Music in the music pool + SA2B: Only SA2B music will be played + SADX: Only SADX music will be played + Both: Both SA2B and SADX music will be played + NOTE: This option requires the player to own a PC copy of SADX and to follow the addition steps in the setup guide. + """ + display_name = "SADX Music" + option_sa2b = 0 + option_sadx = 1 + option_both = 2 + default = 0 + + @classmethod + def get_option_name(cls, value) -> str: + if cls.auto_display_name and value != 2: + return cls.name_lookup[value].upper() + else: + return cls.name_lookup[value] + + class MusicShuffle(Choice): """ What type of Music Shuffle is used Off: No music is shuffled. Levels: Level music is shuffled. Full: Level, Menu, and Additional music is shuffled. + Singularity: Level, Menu, and Additional music is all replaced with a single random song. """ display_name = "Music Shuffle Type" option_none = 0 option_levels = 1 option_full = 2 + option_singularity = 3 + default = 0 + + +class Narrator(Choice): + """ + Which menu narrator is used + """ + display_name = "Narrator" + option_default = 0 + option_shadow = 1 + option_rouge = 2 + option_eggman = 3 + option_maria = 4 + option_secretary = 5 + option_omochao = 6 + option_amy = 7 + option_tails = 8 + option_knuckles = 9 + option_sonic = 10 + default = 0 + + +class LogicDifficulty(Choice): + """ + What set of Upgrade Requirement logic to use + Standard: The logic assumes the "intended" usage of Upgrades to progress through levels + Hard: Some simple skips or sequence breaks may be required + """ + display_name = "Logic Difficulty" + option_standard = 0 + option_hard = 1 default = 0 sa2b_options: typing.Dict[str, type(Option)] = { - "include_missions": IncludeMissions, + "goal": Goal, + "mission_shuffle": MissionShuffle, + "keysanity": Keysanity, + "whistlesanity": Whistlesanity, + "beetlesanity": Beetlesanity, "required_rank": RequiredRank, "emblem_percentage_for_cannons_core": EmblemPercentageForCannonsCore, + "required_cannons_core_missions": RequiredCannonsCoreMissions, "number_of_level_gates": NumberOfLevelGates, "level_gate_distribution": LevelGateDistribution, "level_gate_costs": LevelGateCosts, @@ -202,6 +530,37 @@ class MusicShuffle(Choice): "timestop_trap_weight": TimestopTrapWeight, "confusion_trap_weight": ConfusionTrapWeight, "tiny_trap_weight": TinyTrapWeight, + "gravity_trap_weight": GravityTrapWeight, + "exposition_trap_weight": ExpositionTrapWeight, + #"darkness_trap_weight": DarknessTrapWeight, + "sadx_music": SADXMusic, "music_shuffle": MusicShuffle, + "narrator": Narrator, + "logic_difficulty": LogicDifficulty, + "speed_mission_count": SpeedMissionCount, + "speed_mission_2": SpeedMission2, + "speed_mission_3": SpeedMission3, + "speed_mission_4": SpeedMission4, + "speed_mission_5": SpeedMission5, + "mech_mission_count": MechMissionCount, + "mech_mission_2": MechMission2, + "mech_mission_3": MechMission3, + "mech_mission_4": MechMission4, + "mech_mission_5": MechMission5, + "hunt_mission_count": HuntMissionCount, + "hunt_mission_2": HuntMission2, + "hunt_mission_3": HuntMission3, + "hunt_mission_4": HuntMission4, + "hunt_mission_5": HuntMission5, + "kart_mission_count": KartMissionCount, + "kart_mission_2": KartMission2, + "kart_mission_3": KartMission3, + "kart_mission_4": KartMission4, + "kart_mission_5": KartMission5, + "cannons_core_mission_count": CannonsCoreMissionCount, + "cannons_core_mission_2": CannonsCoreMission2, + "cannons_core_mission_3": CannonsCoreMission3, + "cannons_core_mission_4": CannonsCoreMission4, + "cannons_core_mission_5": CannonsCoreMission5, "death_link": DeathLink, } diff --git a/worlds/sa2b/Regions.py b/worlds/sa2b/Regions.py index dc32bf9e8c5d..42688153dd6c 100644 --- a/worlds/sa2b/Regions.py +++ b/worlds/sa2b/Regions.py @@ -87,18 +87,34 @@ def __init__(self, emblems): def create_regions(world, player: int, active_locations): - menu_region = create_region(world, player, active_locations, 'Menu', None, None) - gate_0_region = create_region(world, player, active_locations, 'Gate 0', None, None) - gate_1_region = create_region(world, player, active_locations, 'Gate 1', None, None) - gate_2_region = create_region(world, player, active_locations, 'Gate 2', None, None) - gate_3_region = create_region(world, player, active_locations, 'Gate 3', None, None) - gate_4_region = create_region(world, player, active_locations, 'Gate 4', None, None) - gate_5_region = create_region(world, player, active_locations, 'Gate 5', None, None) - gate_1_boss_region = create_region(world, player, active_locations, 'Gate 1 Boss', [LocationName.gate_1_boss], None) - gate_2_boss_region = create_region(world, player, active_locations, 'Gate 2 Boss', [LocationName.gate_2_boss], None) - gate_3_boss_region = create_region(world, player, active_locations, 'Gate 3 Boss', [LocationName.gate_3_boss], None) - gate_4_boss_region = create_region(world, player, active_locations, 'Gate 4 Boss', [LocationName.gate_4_boss], None) - gate_5_boss_region = create_region(world, player, active_locations, 'Gate 5 Boss', [LocationName.gate_5_boss], None) + menu_region = create_region(world, player, active_locations, 'Menu', None) + + gate_0_region = create_region(world, player, active_locations, 'Gate 0', None) + + if world.number_of_level_gates[player].value >= 1: + gate_1_boss_region = create_region(world, player, active_locations, 'Gate 1 Boss', [LocationName.gate_1_boss]) + gate_1_region = create_region(world, player, active_locations, 'Gate 1', None) + world.regions += [gate_1_region, gate_1_boss_region] + + if world.number_of_level_gates[player].value >= 2: + gate_2_boss_region = create_region(world, player, active_locations, 'Gate 2 Boss', [LocationName.gate_2_boss]) + gate_2_region = create_region(world, player, active_locations, 'Gate 2', None) + world.regions += [gate_2_region, gate_2_boss_region] + + if world.number_of_level_gates[player].value >= 3: + gate_3_boss_region = create_region(world, player, active_locations, 'Gate 3 Boss', [LocationName.gate_3_boss]) + gate_3_region = create_region(world, player, active_locations, 'Gate 3', None) + world.regions += [gate_3_region, gate_3_boss_region] + + if world.number_of_level_gates[player].value >= 4: + gate_4_boss_region = create_region(world, player, active_locations, 'Gate 4 Boss', [LocationName.gate_4_boss]) + gate_4_region = create_region(world, player, active_locations, 'Gate 4', None) + world.regions += [gate_4_region, gate_4_boss_region] + + if world.number_of_level_gates[player].value >= 5: + gate_5_boss_region = create_region(world, player, active_locations, 'Gate 5 Boss', [LocationName.gate_5_boss]) + gate_5_region = create_region(world, player, active_locations, 'Gate 5', None) + world.regions += [gate_5_region, gate_5_boss_region] city_escape_region_locations = [ LocationName.city_escape_1, @@ -106,10 +122,23 @@ def create_regions(world, player: int, active_locations): LocationName.city_escape_3, LocationName.city_escape_4, LocationName.city_escape_5, + LocationName.city_escape_chao_1, + LocationName.city_escape_chao_2, + LocationName.city_escape_chao_3, + LocationName.city_escape_pipe_1, + LocationName.city_escape_pipe_2, + LocationName.city_escape_pipe_3, + LocationName.city_escape_pipe_4, + LocationName.city_escape_hidden_1, + LocationName.city_escape_hidden_2, + LocationName.city_escape_hidden_3, + LocationName.city_escape_hidden_4, + LocationName.city_escape_hidden_5, + LocationName.city_escape_beetle, LocationName.city_escape_upgrade, ] city_escape_region = create_region(world, player, active_locations, LocationName.city_escape_region, - city_escape_region_locations, None) + city_escape_region_locations) metal_harbor_region_locations = [ LocationName.metal_harbor_1, @@ -117,10 +146,15 @@ def create_regions(world, player: int, active_locations): LocationName.metal_harbor_3, LocationName.metal_harbor_4, LocationName.metal_harbor_5, + LocationName.metal_harbor_chao_1, + LocationName.metal_harbor_chao_2, + LocationName.metal_harbor_chao_3, + LocationName.metal_harbor_pipe_1, + LocationName.metal_harbor_beetle, LocationName.metal_harbor_upgrade, ] metal_harbor_region = create_region(world, player, active_locations, LocationName.metal_harbor_region, - metal_harbor_region_locations, None) + metal_harbor_region_locations) green_forest_region_locations = [ LocationName.green_forest_1, @@ -128,10 +162,20 @@ def create_regions(world, player: int, active_locations): LocationName.green_forest_3, LocationName.green_forest_4, LocationName.green_forest_5, + LocationName.green_forest_chao_1, + LocationName.green_forest_chao_2, + LocationName.green_forest_chao_3, + LocationName.green_forest_pipe_1, + LocationName.green_forest_pipe_2, + LocationName.green_forest_hidden_1, + LocationName.green_forest_hidden_2, + LocationName.green_forest_hidden_3, + LocationName.green_forest_hidden_4, + LocationName.green_forest_beetle, LocationName.green_forest_upgrade, ] green_forest_region = create_region(world, player, active_locations, LocationName.green_forest_region, - green_forest_region_locations, None) + green_forest_region_locations) pyramid_cave_region_locations = [ LocationName.pyramid_cave_1, @@ -139,10 +183,18 @@ def create_regions(world, player: int, active_locations): LocationName.pyramid_cave_3, LocationName.pyramid_cave_4, LocationName.pyramid_cave_5, + LocationName.pyramid_cave_chao_1, + LocationName.pyramid_cave_chao_2, + LocationName.pyramid_cave_chao_3, + LocationName.pyramid_cave_pipe_1, + LocationName.pyramid_cave_pipe_2, + LocationName.pyramid_cave_pipe_3, + LocationName.pyramid_cave_pipe_4, + LocationName.pyramid_cave_beetle, LocationName.pyramid_cave_upgrade, ] pyramid_cave_region = create_region(world, player, active_locations, LocationName.pyramid_cave_region, - pyramid_cave_region_locations, None) + pyramid_cave_region_locations) crazy_gadget_region_locations = [ LocationName.crazy_gadget_1, @@ -150,10 +202,19 @@ def create_regions(world, player: int, active_locations): LocationName.crazy_gadget_3, LocationName.crazy_gadget_4, LocationName.crazy_gadget_5, + LocationName.crazy_gadget_chao_1, + LocationName.crazy_gadget_chao_2, + LocationName.crazy_gadget_chao_3, + LocationName.crazy_gadget_pipe_1, + LocationName.crazy_gadget_pipe_2, + LocationName.crazy_gadget_pipe_3, + LocationName.crazy_gadget_pipe_4, + LocationName.crazy_gadget_hidden_1, + LocationName.crazy_gadget_beetle, LocationName.crazy_gadget_upgrade, ] crazy_gadget_region = create_region(world, player, active_locations, LocationName.crazy_gadget_region, - crazy_gadget_region_locations, None) + crazy_gadget_region_locations) final_rush_region_locations = [ LocationName.final_rush_1, @@ -161,10 +222,16 @@ def create_regions(world, player: int, active_locations): LocationName.final_rush_3, LocationName.final_rush_4, LocationName.final_rush_5, + LocationName.final_rush_chao_1, + LocationName.final_rush_chao_2, + LocationName.final_rush_chao_3, + LocationName.final_rush_pipe_1, + LocationName.final_rush_pipe_2, + LocationName.final_rush_beetle, LocationName.final_rush_upgrade, ] final_rush_region = create_region(world, player, active_locations, LocationName.final_rush_region, - final_rush_region_locations, None) + final_rush_region_locations) prison_lane_region_locations = [ LocationName.prison_lane_1, @@ -172,10 +239,20 @@ def create_regions(world, player: int, active_locations): LocationName.prison_lane_3, LocationName.prison_lane_4, LocationName.prison_lane_5, + LocationName.prison_lane_chao_1, + LocationName.prison_lane_chao_2, + LocationName.prison_lane_chao_3, + LocationName.prison_lane_pipe_1, + LocationName.prison_lane_pipe_2, + LocationName.prison_lane_pipe_3, + LocationName.prison_lane_hidden_1, + LocationName.prison_lane_hidden_2, + LocationName.prison_lane_hidden_3, + LocationName.prison_lane_beetle, LocationName.prison_lane_upgrade, ] prison_lane_region = create_region(world, player, active_locations, LocationName.prison_lane_region, - prison_lane_region_locations, None) + prison_lane_region_locations) mission_street_region_locations = [ LocationName.mission_street_1, @@ -183,10 +260,20 @@ def create_regions(world, player: int, active_locations): LocationName.mission_street_3, LocationName.mission_street_4, LocationName.mission_street_5, + LocationName.mission_street_chao_1, + LocationName.mission_street_chao_2, + LocationName.mission_street_chao_3, + LocationName.mission_street_pipe_1, + LocationName.mission_street_pipe_2, + LocationName.mission_street_pipe_3, + LocationName.mission_street_hidden_1, + LocationName.mission_street_hidden_2, + LocationName.mission_street_hidden_3, + LocationName.mission_street_beetle, LocationName.mission_street_upgrade, ] mission_street_region = create_region(world, player, active_locations, LocationName.mission_street_region, - mission_street_region_locations, None) + mission_street_region_locations) route_101_region_locations = [ LocationName.route_101_1, @@ -196,7 +283,7 @@ def create_regions(world, player: int, active_locations): LocationName.route_101_5, ] route_101_region = create_region(world, player, active_locations, LocationName.route_101_region, - route_101_region_locations, None) + route_101_region_locations) hidden_base_region_locations = [ LocationName.hidden_base_1, @@ -204,10 +291,18 @@ def create_regions(world, player: int, active_locations): LocationName.hidden_base_3, LocationName.hidden_base_4, LocationName.hidden_base_5, + LocationName.hidden_base_chao_1, + LocationName.hidden_base_chao_2, + LocationName.hidden_base_pipe_1, + LocationName.hidden_base_pipe_2, + LocationName.hidden_base_pipe_3, + LocationName.hidden_base_pipe_4, + LocationName.hidden_base_pipe_5, + LocationName.hidden_base_beetle, LocationName.hidden_base_upgrade, ] hidden_base_region = create_region(world, player, active_locations, LocationName.hidden_base_region, - hidden_base_region_locations, None) + hidden_base_region_locations) eternal_engine_region_locations = [ LocationName.eternal_engine_1, @@ -215,10 +310,19 @@ def create_regions(world, player: int, active_locations): LocationName.eternal_engine_3, LocationName.eternal_engine_4, LocationName.eternal_engine_5, + LocationName.eternal_engine_chao_1, + LocationName.eternal_engine_chao_2, + LocationName.eternal_engine_chao_3, + LocationName.eternal_engine_pipe_1, + LocationName.eternal_engine_pipe_2, + LocationName.eternal_engine_pipe_3, + LocationName.eternal_engine_pipe_4, + LocationName.eternal_engine_pipe_5, + LocationName.eternal_engine_beetle, LocationName.eternal_engine_upgrade, ] eternal_engine_region = create_region(world, player, active_locations, LocationName.eternal_engine_region, - eternal_engine_region_locations, None) + eternal_engine_region_locations) wild_canyon_region_locations = [ LocationName.wild_canyon_1, @@ -226,10 +330,17 @@ def create_regions(world, player: int, active_locations): LocationName.wild_canyon_3, LocationName.wild_canyon_4, LocationName.wild_canyon_5, + LocationName.wild_canyon_chao_1, + LocationName.wild_canyon_chao_2, + LocationName.wild_canyon_chao_3, + LocationName.wild_canyon_pipe_1, + LocationName.wild_canyon_pipe_2, + LocationName.wild_canyon_pipe_3, + LocationName.wild_canyon_beetle, LocationName.wild_canyon_upgrade, ] wild_canyon_region = create_region(world, player, active_locations, LocationName.wild_canyon_region, - wild_canyon_region_locations, None) + wild_canyon_region_locations) pumpkin_hill_region_locations = [ LocationName.pumpkin_hill_1, @@ -237,10 +348,15 @@ def create_regions(world, player: int, active_locations): LocationName.pumpkin_hill_3, LocationName.pumpkin_hill_4, LocationName.pumpkin_hill_5, + LocationName.pumpkin_hill_chao_1, + LocationName.pumpkin_hill_chao_2, + LocationName.pumpkin_hill_chao_3, + LocationName.pumpkin_hill_pipe_1, + LocationName.pumpkin_hill_hidden_1, LocationName.pumpkin_hill_upgrade, ] pumpkin_hill_region = create_region(world, player, active_locations, LocationName.pumpkin_hill_region, - pumpkin_hill_region_locations, None) + pumpkin_hill_region_locations) aquatic_mine_region_locations = [ LocationName.aquatic_mine_1, @@ -248,10 +364,17 @@ def create_regions(world, player: int, active_locations): LocationName.aquatic_mine_3, LocationName.aquatic_mine_4, LocationName.aquatic_mine_5, + LocationName.aquatic_mine_chao_1, + LocationName.aquatic_mine_chao_2, + LocationName.aquatic_mine_chao_3, + LocationName.aquatic_mine_pipe_1, + LocationName.aquatic_mine_pipe_2, + LocationName.aquatic_mine_pipe_3, + LocationName.aquatic_mine_beetle, LocationName.aquatic_mine_upgrade, ] aquatic_mine_region = create_region(world, player, active_locations, LocationName.aquatic_mine_region, - aquatic_mine_region_locations, None) + aquatic_mine_region_locations) death_chamber_region_locations = [ LocationName.death_chamber_1, @@ -259,10 +382,19 @@ def create_regions(world, player: int, active_locations): LocationName.death_chamber_3, LocationName.death_chamber_4, LocationName.death_chamber_5, + LocationName.death_chamber_chao_1, + LocationName.death_chamber_chao_2, + LocationName.death_chamber_chao_3, + LocationName.death_chamber_pipe_1, + LocationName.death_chamber_pipe_2, + LocationName.death_chamber_pipe_3, + LocationName.death_chamber_hidden_1, + LocationName.death_chamber_hidden_2, + LocationName.death_chamber_beetle, LocationName.death_chamber_upgrade, ] death_chamber_region = create_region(world, player, active_locations, LocationName.death_chamber_region, - death_chamber_region_locations, None) + death_chamber_region_locations) meteor_herd_region_locations = [ LocationName.meteor_herd_1, @@ -270,10 +402,17 @@ def create_regions(world, player: int, active_locations): LocationName.meteor_herd_3, LocationName.meteor_herd_4, LocationName.meteor_herd_5, + LocationName.meteor_herd_chao_1, + LocationName.meteor_herd_chao_2, + LocationName.meteor_herd_chao_3, + LocationName.meteor_herd_pipe_1, + LocationName.meteor_herd_pipe_2, + LocationName.meteor_herd_pipe_3, + LocationName.meteor_herd_beetle, LocationName.meteor_herd_upgrade, ] meteor_herd_region = create_region(world, player, active_locations, LocationName.meteor_herd_region, - meteor_herd_region_locations, None) + meteor_herd_region_locations) radical_highway_region_locations = [ LocationName.radical_highway_1, @@ -281,10 +420,20 @@ def create_regions(world, player: int, active_locations): LocationName.radical_highway_3, LocationName.radical_highway_4, LocationName.radical_highway_5, + LocationName.radical_highway_chao_1, + LocationName.radical_highway_chao_2, + LocationName.radical_highway_chao_3, + LocationName.radical_highway_pipe_1, + LocationName.radical_highway_pipe_2, + LocationName.radical_highway_pipe_3, + LocationName.radical_highway_hidden_1, + LocationName.radical_highway_hidden_2, + LocationName.radical_highway_hidden_3, + LocationName.radical_highway_beetle, LocationName.radical_highway_upgrade, ] radical_highway_region = create_region(world, player, active_locations, LocationName.radical_highway_region, - radical_highway_region_locations, None) + radical_highway_region_locations) white_jungle_region_locations = [ LocationName.white_jungle_1, @@ -292,10 +441,21 @@ def create_regions(world, player: int, active_locations): LocationName.white_jungle_3, LocationName.white_jungle_4, LocationName.white_jungle_5, + LocationName.white_jungle_chao_1, + LocationName.white_jungle_chao_2, + LocationName.white_jungle_chao_3, + LocationName.white_jungle_pipe_1, + LocationName.white_jungle_pipe_2, + LocationName.white_jungle_pipe_3, + LocationName.white_jungle_pipe_4, + LocationName.white_jungle_hidden_1, + LocationName.white_jungle_hidden_2, + LocationName.white_jungle_hidden_3, + LocationName.white_jungle_beetle, LocationName.white_jungle_upgrade, ] white_jungle_region = create_region(world, player, active_locations, LocationName.white_jungle_region, - white_jungle_region_locations, None) + white_jungle_region_locations) sky_rail_region_locations = [ LocationName.sky_rail_1, @@ -303,10 +463,20 @@ def create_regions(world, player: int, active_locations): LocationName.sky_rail_3, LocationName.sky_rail_4, LocationName.sky_rail_5, + LocationName.sky_rail_chao_1, + LocationName.sky_rail_chao_2, + LocationName.sky_rail_chao_3, + LocationName.sky_rail_pipe_1, + LocationName.sky_rail_pipe_2, + LocationName.sky_rail_pipe_3, + LocationName.sky_rail_pipe_4, + LocationName.sky_rail_pipe_5, + LocationName.sky_rail_pipe_6, + LocationName.sky_rail_beetle, LocationName.sky_rail_upgrade, ] sky_rail_region = create_region(world, player, active_locations, LocationName.sky_rail_region, - sky_rail_region_locations, None) + sky_rail_region_locations) final_chase_region_locations = [ LocationName.final_chase_1, @@ -314,10 +484,17 @@ def create_regions(world, player: int, active_locations): LocationName.final_chase_3, LocationName.final_chase_4, LocationName.final_chase_5, + LocationName.final_chase_chao_1, + LocationName.final_chase_chao_2, + LocationName.final_chase_chao_3, + LocationName.final_chase_pipe_1, + LocationName.final_chase_pipe_2, + LocationName.final_chase_pipe_3, + LocationName.final_chase_beetle, LocationName.final_chase_upgrade, ] final_chase_region = create_region(world, player, active_locations, LocationName.final_chase_region, - final_chase_region_locations, None) + final_chase_region_locations) iron_gate_region_locations = [ LocationName.iron_gate_1, @@ -325,10 +502,19 @@ def create_regions(world, player: int, active_locations): LocationName.iron_gate_3, LocationName.iron_gate_4, LocationName.iron_gate_5, + LocationName.iron_gate_chao_1, + LocationName.iron_gate_chao_2, + LocationName.iron_gate_chao_3, + LocationName.iron_gate_pipe_1, + LocationName.iron_gate_pipe_2, + LocationName.iron_gate_pipe_3, + LocationName.iron_gate_pipe_4, + LocationName.iron_gate_pipe_5, + LocationName.iron_gate_beetle, LocationName.iron_gate_upgrade, ] iron_gate_region = create_region(world, player, active_locations, LocationName.iron_gate_region, - iron_gate_region_locations, None) + iron_gate_region_locations) sand_ocean_region_locations = [ LocationName.sand_ocean_1, @@ -336,10 +522,19 @@ def create_regions(world, player: int, active_locations): LocationName.sand_ocean_3, LocationName.sand_ocean_4, LocationName.sand_ocean_5, + LocationName.sand_ocean_chao_1, + LocationName.sand_ocean_chao_2, + LocationName.sand_ocean_chao_3, + LocationName.sand_ocean_pipe_1, + LocationName.sand_ocean_pipe_2, + LocationName.sand_ocean_pipe_3, + LocationName.sand_ocean_pipe_4, + LocationName.sand_ocean_pipe_5, + LocationName.sand_ocean_beetle, LocationName.sand_ocean_upgrade, ] sand_ocean_region = create_region(world, player, active_locations, LocationName.sand_ocean_region, - sand_ocean_region_locations, None) + sand_ocean_region_locations) lost_colony_region_locations = [ LocationName.lost_colony_1, @@ -347,10 +542,17 @@ def create_regions(world, player: int, active_locations): LocationName.lost_colony_3, LocationName.lost_colony_4, LocationName.lost_colony_5, + LocationName.lost_colony_chao_1, + LocationName.lost_colony_chao_2, + LocationName.lost_colony_chao_3, + LocationName.lost_colony_pipe_1, + LocationName.lost_colony_pipe_2, + LocationName.lost_colony_hidden_1, + LocationName.lost_colony_beetle, LocationName.lost_colony_upgrade, ] lost_colony_region = create_region(world, player, active_locations, LocationName.lost_colony_region, - lost_colony_region_locations, None) + lost_colony_region_locations) weapons_bed_region_locations = [ LocationName.weapons_bed_1, @@ -358,10 +560,18 @@ def create_regions(world, player: int, active_locations): LocationName.weapons_bed_3, LocationName.weapons_bed_4, LocationName.weapons_bed_5, + LocationName.weapons_bed_chao_1, + LocationName.weapons_bed_chao_2, + LocationName.weapons_bed_chao_3, + LocationName.weapons_bed_pipe_1, + LocationName.weapons_bed_pipe_2, + LocationName.weapons_bed_pipe_3, + LocationName.weapons_bed_pipe_4, + LocationName.weapons_bed_pipe_5, LocationName.weapons_bed_upgrade, ] weapons_bed_region = create_region(world, player, active_locations, LocationName.weapons_bed_region, - weapons_bed_region_locations, None) + weapons_bed_region_locations) cosmic_wall_region_locations = [ LocationName.cosmic_wall_1, @@ -369,10 +579,19 @@ def create_regions(world, player: int, active_locations): LocationName.cosmic_wall_3, LocationName.cosmic_wall_4, LocationName.cosmic_wall_5, + LocationName.cosmic_wall_chao_1, + LocationName.cosmic_wall_chao_2, + LocationName.cosmic_wall_chao_3, + LocationName.cosmic_wall_pipe_1, + LocationName.cosmic_wall_pipe_2, + LocationName.cosmic_wall_pipe_3, + LocationName.cosmic_wall_pipe_4, + LocationName.cosmic_wall_pipe_5, + LocationName.cosmic_wall_beetle, LocationName.cosmic_wall_upgrade, ] cosmic_wall_region = create_region(world, player, active_locations, LocationName.cosmic_wall_region, - cosmic_wall_region_locations, None) + cosmic_wall_region_locations) dry_lagoon_region_locations = [ LocationName.dry_lagoon_1, @@ -380,10 +599,16 @@ def create_regions(world, player: int, active_locations): LocationName.dry_lagoon_3, LocationName.dry_lagoon_4, LocationName.dry_lagoon_5, + LocationName.dry_lagoon_chao_1, + LocationName.dry_lagoon_chao_2, + LocationName.dry_lagoon_chao_3, + LocationName.dry_lagoon_pipe_1, + LocationName.dry_lagoon_hidden_1, + LocationName.dry_lagoon_beetle, LocationName.dry_lagoon_upgrade, ] dry_lagoon_region = create_region(world, player, active_locations, LocationName.dry_lagoon_region, - dry_lagoon_region_locations, None) + dry_lagoon_region_locations) egg_quarters_region_locations = [ LocationName.egg_quarters_1, @@ -391,10 +616,18 @@ def create_regions(world, player: int, active_locations): LocationName.egg_quarters_3, LocationName.egg_quarters_4, LocationName.egg_quarters_5, + LocationName.egg_quarters_chao_1, + LocationName.egg_quarters_chao_2, + LocationName.egg_quarters_chao_3, + LocationName.egg_quarters_pipe_1, + LocationName.egg_quarters_pipe_2, + LocationName.egg_quarters_hidden_1, + LocationName.egg_quarters_hidden_2, + LocationName.egg_quarters_beetle, LocationName.egg_quarters_upgrade, ] egg_quarters_region = create_region(world, player, active_locations, LocationName.egg_quarters_region, - egg_quarters_region_locations, None) + egg_quarters_region_locations) security_hall_region_locations = [ LocationName.security_hall_1, @@ -402,10 +635,16 @@ def create_regions(world, player: int, active_locations): LocationName.security_hall_3, LocationName.security_hall_4, LocationName.security_hall_5, + LocationName.security_hall_chao_1, + LocationName.security_hall_chao_2, + LocationName.security_hall_chao_3, + LocationName.security_hall_pipe_1, + LocationName.security_hall_hidden_1, + LocationName.security_hall_beetle, LocationName.security_hall_upgrade, ] security_hall_region = create_region(world, player, active_locations, LocationName.security_hall_region, - security_hall_region_locations, None) + security_hall_region_locations) route_280_region_locations = [ LocationName.route_280_1, @@ -415,7 +654,7 @@ def create_regions(world, player: int, active_locations): LocationName.route_280_5, ] route_280_region = create_region(world, player, active_locations, LocationName.route_280_region, - route_280_region_locations, None) + route_280_region_locations) mad_space_region_locations = [ LocationName.mad_space_1, @@ -423,10 +662,18 @@ def create_regions(world, player: int, active_locations): LocationName.mad_space_3, LocationName.mad_space_4, LocationName.mad_space_5, + LocationName.mad_space_chao_1, + LocationName.mad_space_chao_2, + LocationName.mad_space_chao_3, + LocationName.mad_space_pipe_1, + LocationName.mad_space_pipe_2, + LocationName.mad_space_pipe_3, + LocationName.mad_space_pipe_4, + LocationName.mad_space_beetle, LocationName.mad_space_upgrade, ] mad_space_region = create_region(world, player, active_locations, LocationName.mad_space_region, - mad_space_region_locations, None) + mad_space_region_locations) cannon_core_region_locations = [ LocationName.cannon_core_1, @@ -434,9 +681,19 @@ def create_regions(world, player: int, active_locations): LocationName.cannon_core_3, LocationName.cannon_core_4, LocationName.cannon_core_5, + LocationName.cannon_core_chao_1, + LocationName.cannon_core_chao_2, + LocationName.cannon_core_chao_3, + LocationName.cannon_core_pipe_1, + LocationName.cannon_core_pipe_2, + LocationName.cannon_core_pipe_3, + LocationName.cannon_core_pipe_4, + LocationName.cannon_core_pipe_5, + LocationName.cannon_core_hidden_1, + LocationName.cannon_core_beetle, ] cannon_core_region = create_region(world, player, active_locations, LocationName.cannon_core_region, - cannon_core_region_locations, None) + cannon_core_region_locations) chao_garden_beginner_region_locations = [ LocationName.chao_race_crab_pool_1, @@ -455,9 +712,38 @@ def create_regions(world, player: int, active_locations): LocationName.chao_beginner_karate, ] chao_garden_beginner_region = create_region(world, player, active_locations, LocationName.chao_garden_beginner_region, - chao_garden_beginner_region_locations, None) + chao_garden_beginner_region_locations) chao_garden_intermediate_region_locations = [ + LocationName.chao_race_challenge_1, + LocationName.chao_race_challenge_2, + LocationName.chao_race_challenge_3, + LocationName.chao_race_challenge_4, + LocationName.chao_race_challenge_5, + LocationName.chao_race_challenge_6, + LocationName.chao_race_challenge_7, + LocationName.chao_race_challenge_8, + LocationName.chao_race_challenge_9, + LocationName.chao_race_challenge_10, + LocationName.chao_race_challenge_11, + LocationName.chao_race_challenge_12, + + LocationName.chao_race_hero_1, + LocationName.chao_race_hero_2, + LocationName.chao_race_hero_3, + LocationName.chao_race_hero_4, + + LocationName.chao_race_dark_1, + LocationName.chao_race_dark_2, + LocationName.chao_race_dark_3, + LocationName.chao_race_dark_4, + + LocationName.chao_standard_karate, + ] + chao_garden_intermediate_region = create_region(world, player, active_locations, LocationName.chao_garden_intermediate_region, + chao_garden_intermediate_region_locations) + + chao_garden_expert_region_locations = [ LocationName.chao_race_aquamarine_1, LocationName.chao_race_aquamarine_2, LocationName.chao_race_aquamarine_3, @@ -489,61 +775,34 @@ def create_regions(world, player: int, active_locations): LocationName.chao_race_diamond_4, LocationName.chao_race_diamond_5, - LocationName.chao_standard_karate, - ] - chao_garden_intermediate_region = create_region(world, player, active_locations, LocationName.chao_garden_intermediate_region, - chao_garden_intermediate_region_locations, None) - - chao_garden_expert_region_locations = [ - LocationName.chao_race_challenge_1, - LocationName.chao_race_challenge_2, - LocationName.chao_race_challenge_3, - LocationName.chao_race_challenge_4, - LocationName.chao_race_challenge_5, - LocationName.chao_race_challenge_6, - LocationName.chao_race_challenge_7, - LocationName.chao_race_challenge_8, - LocationName.chao_race_challenge_9, - LocationName.chao_race_challenge_10, - LocationName.chao_race_challenge_11, - LocationName.chao_race_challenge_12, - - LocationName.chao_race_hero_1, - LocationName.chao_race_hero_2, - LocationName.chao_race_hero_3, - LocationName.chao_race_hero_4, - - LocationName.chao_race_dark_1, - LocationName.chao_race_dark_2, - LocationName.chao_race_dark_3, - LocationName.chao_race_dark_4, - LocationName.chao_expert_karate, LocationName.chao_super_karate, ] chao_garden_expert_region = create_region(world, player, active_locations, LocationName.chao_garden_expert_region, - chao_garden_expert_region_locations, None) + chao_garden_expert_region_locations) + + if world.goal[player] == 0 or world.goal[player] == 2: + biolizard_region_locations = [ + LocationName.finalhazard, + ] + biolizard_region = create_region(world, player, active_locations, LocationName.biolizard_region, + biolizard_region_locations) + world.regions += [biolizard_region] + + if world.goal[player] == 1 or world.goal[player] == 2: + green_hill_region_locations = [ + LocationName.green_hill, + LocationName.green_hill_chao_1, + ] + green_hill_region = create_region(world, player, active_locations, LocationName.green_hill_region, + green_hill_region_locations) + world.regions += [green_hill_region] - biolizard_region_locations = [ - LocationName.biolizard, - ] - biolizard_region = create_region(world, player, active_locations, LocationName.biolizard_region, - biolizard_region_locations, None) # Set up the regions correctly. world.regions += [ menu_region, gate_0_region, - gate_1_region, - gate_2_region, - gate_3_region, - gate_4_region, - gate_5_region, - gate_1_boss_region, - gate_2_boss_region, - gate_3_boss_region, - gate_4_boss_region, - gate_5_boss_region, city_escape_region, metal_harbor_region, green_forest_region, @@ -578,19 +837,35 @@ def create_regions(world, player: int, active_locations): chao_garden_beginner_region, chao_garden_intermediate_region, chao_garden_expert_region, - biolizard_region, ] -def connect_regions(world, player, gates: typing.List[LevelGate], cannon_core_emblems, gate_bosses): +def connect_regions(world, player, gates: typing.List[LevelGate], cannon_core_emblems, gate_bosses, first_cannons_core_mission: str, final_cannons_core_mission: str): names: typing.Dict[str, int] = {} connect(world, player, names, 'Menu', LocationName.gate_0_region) connect(world, player, names, LocationName.gate_0_region, LocationName.cannon_core_region, lambda state: (state.has(ItemName.emblem, player, cannon_core_emblems))) - connect(world, player, names, LocationName.cannon_core_region, LocationName.biolizard_region, - lambda state: (state.can_reach(LocationName.cannon_core_1, "Location", player))) + if world.goal[player] == 0: + required_mission_name = first_cannons_core_mission + + if world.required_cannons_core_missions[player].value == 1: + required_mission_name = final_cannons_core_mission + + connect(world, player, names, LocationName.cannon_core_region, LocationName.biolizard_region, + lambda state: (state.can_reach(required_mission_name, "Location", player))) + elif world.goal[player] == 1 or world.goal[player] == 2: + connect(world, player, names, 'Menu', LocationName.green_hill_region, + lambda state: (state.has(ItemName.white_emerald, player) and + state.has(ItemName.red_emerald, player) and + state.has(ItemName.cyan_emerald, player) and + state.has(ItemName.purple_emerald, player) and + state.has(ItemName.green_emerald, player) and + state.has(ItemName.yellow_emerald, player) and + state.has(ItemName.blue_emerald, player))) + if world.goal[player] == 2: + connect(world, player, names, LocationName.green_hill_region, LocationName.biolizard_region) for i in range(len(gates[0].gate_levels)): connect(world, player, names, LocationName.gate_0_region, shuffleable_regions[gates[0].gate_levels[i]]) @@ -687,8 +962,7 @@ def connect_regions(world, player, gates: typing.List[LevelGate], cannon_core_em connect(world, player, names, LocationName.gate_4_region, LocationName.chao_garden_expert_region) -def create_region(world: MultiWorld, player: int, active_locations, name: str, locations=None, exits=None): - # Shamelessly stolen from the ROR2 definition +def create_region(world: MultiWorld, player: int, active_locations, name: str, locations=None): ret = Region(name, None, name, player) ret.multiworld = world if locations: @@ -697,9 +971,6 @@ def create_region(world: MultiWorld, player: int, active_locations, name: str, l if loc_id: location = SA2BLocation(player, location, loc_id, ret) ret.locations.append(location) - if exits: - for exit in exits: - ret.exits.append(Entrance(player, exit, ret)) return ret diff --git a/worlds/sa2b/Rules.py b/worlds/sa2b/Rules.py index 3b09712e0fc0..7b27b73a298f 100644 --- a/worlds/sa2b/Rules.py +++ b/worlds/sa2b/Rules.py @@ -2,352 +2,858 @@ from BaseClasses import MultiWorld from .Names import LocationName, ItemName -from .Locations import first_mission_location_table, second_mission_location_table, third_mission_location_table, \ - fourth_mission_location_table, fifth_mission_location_table, \ - upgrade_location_table, boss_gate_set +from .Locations import boss_gate_set from ..AutoWorld import LogicMixin -from ..generic.Rules import add_rule, set_rule +from ..generic.Rules import add_rule, set_rule, CollectionRule from .GateBosses import boss_has_requirement +from .Missions import stage_name_prefixes, mission_orders -def set_mission_progress_rules(world: MultiWorld, player: int): - for (k1, v1), (k2, v2), (k3, v3), (k4, v4), (k5, v5) in \ - zip(sorted(first_mission_location_table.items()), - sorted(second_mission_location_table.items()), - sorted(third_mission_location_table.items()), - sorted(fourth_mission_location_table.items()), - sorted(fifth_mission_location_table.items())): +def add_rule_safe(multiworld: MultiWorld, spot_name: str, player: int, rule: CollectionRule): + try: + location = multiworld.get_location(spot_name, player) + except KeyError: + # Do nothing for mission locations that do not exist + pass + else: + add_rule(location, rule) - if world.include_missions[player].value >= 2: - set_rule(world.get_location(k2, player), lambda state, k1=k1: state.can_reach(k1, "Location", player)) - if world.include_missions[player].value >= 3: - set_rule(world.get_location(k3, player), lambda state, k2=k2: state.can_reach(k2, "Location", player)) +def set_mission_progress_rules(world: MultiWorld, player: int, mission_map: typing.Dict[int, int], mission_count_map: typing.Dict[int, int]): + for i in range(31): + mission_count = mission_count_map[i] + mission_order: typing.List[int] = mission_orders[mission_map[i]] + stage_prefix: str = stage_name_prefixes[i] - if world.include_missions[player].value >= 4: - set_rule(world.get_location(k4, player), lambda state, k3=k3: state.can_reach(k3, "Location", player)) + for j in range(mission_count): + if j == 0: + continue - if world.include_missions[player].value >= 5: - set_rule(world.get_location(k5, player), lambda state, k4=k4: state.can_reach(k4, "Location", player)) + mission_number = mission_order[j] + prev_mission_number = mission_order[j - 1] + location_name: str = stage_prefix + str(mission_number) + prev_location_name: str = stage_prefix + str(prev_mission_number) + set_rule(world.get_location(location_name, player), + lambda state, prev_location_name=prev_location_name: state.can_reach(prev_location_name, "Location", player)) -def set_mission_upgrade_rules(world: MultiWorld, player: int): +def set_mission_upgrade_rules_standard(world: MultiWorld, player: int): # Mission 1 Upgrade Requirements - add_rule(world.get_location(LocationName.metal_harbor_1, player), - lambda state: state.has(ItemName.sonic_light_shoes, player)) - add_rule(world.get_location(LocationName.pumpkin_hill_1, player), - lambda state: state.has(ItemName.knuckles_shovel_claws, player)) - add_rule(world.get_location(LocationName.mission_street_1, player), - lambda state: state.has(ItemName.tails_booster, player)) - add_rule(world.get_location(LocationName.aquatic_mine_1, player), + add_rule_safe(world, LocationName.metal_harbor_1, player, + lambda state: state.has(ItemName.sonic_light_shoes, player)) + add_rule_safe(world, LocationName.pumpkin_hill_1, player, + lambda state: state.has(ItemName.knuckles_shovel_claws, player)) + add_rule_safe(world, LocationName.mission_street_1, player, + lambda state: state.has(ItemName.tails_booster, player)) + add_rule_safe(world, LocationName.aquatic_mine_1, player, + lambda state: state.has(ItemName.knuckles_shovel_claws, player)) + add_rule_safe(world, LocationName.hidden_base_1, player, + lambda state: state.has(ItemName.tails_booster, player)) + add_rule_safe(world, LocationName.pyramid_cave_1, player, + lambda state: state.has(ItemName.sonic_bounce_bracelet, player)) + add_rule_safe(world, LocationName.death_chamber_1, player, + lambda state: state.has(ItemName.knuckles_shovel_claws, player) and + state.has(ItemName.knuckles_hammer_gloves, player)) + add_rule_safe(world, LocationName.eternal_engine_1, player, + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.tails_bazooka, player)) + add_rule_safe(world, LocationName.meteor_herd_1, player, + lambda state: state.has(ItemName.knuckles_shovel_claws, player) and + state.has(ItemName.knuckles_hammer_gloves, player)) + add_rule_safe(world, LocationName.crazy_gadget_1, player, + lambda state: state.has(ItemName.sonic_light_shoes, player) and + state.has(ItemName.sonic_bounce_bracelet, player) and + state.has(ItemName.sonic_flame_ring, player)) + add_rule_safe(world, LocationName.final_rush_1, player, + lambda state: state.has(ItemName.sonic_bounce_bracelet, player)) + + add_rule_safe(world, LocationName.egg_quarters_1, player, + lambda state: state.has(ItemName.rouge_pick_nails, player)) + add_rule_safe(world, LocationName.lost_colony_1, player, + lambda state: state.has(ItemName.eggman_jet_engine, player)) + add_rule_safe(world, LocationName.weapons_bed_1, player, + lambda state: state.has(ItemName.eggman_jet_engine, player) and + state.has(ItemName.eggman_large_cannon, player)) + add_rule_safe(world, LocationName.security_hall_1, player, + lambda state: state.has(ItemName.rouge_pick_nails, player)) + add_rule_safe(world, LocationName.white_jungle_1, player, + lambda state: state.has(ItemName.shadow_air_shoes, player)) + add_rule_safe(world, LocationName.mad_space_1, player, + lambda state: state.has(ItemName.rouge_pick_nails, player) and + state.has(ItemName.rouge_iron_boots, player)) + add_rule_safe(world, LocationName.cosmic_wall_1, player, + lambda state: state.has(ItemName.eggman_jet_engine, player)) + + add_rule_safe(world, LocationName.cannon_core_1, player, + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.eggman_jet_engine, player) and + state.has(ItemName.knuckles_hammer_gloves, player) and + state.has(ItemName.knuckles_air_necklace, player) and + state.has(ItemName.sonic_bounce_bracelet, player)) + + # Mission 2 Upgrade Requirements + add_rule_safe(world, LocationName.metal_harbor_2, player, + lambda state: state.has(ItemName.sonic_light_shoes, player)) + add_rule_safe(world, LocationName.mission_street_2, player, + lambda state: state.has(ItemName.tails_booster, player)) + add_rule_safe(world, LocationName.hidden_base_2, player, + lambda state: state.has(ItemName.tails_booster, player)) + add_rule_safe(world, LocationName.death_chamber_2, player, + lambda state: state.has(ItemName.knuckles_shovel_claws, player) and + state.has(ItemName.knuckles_hammer_gloves, player)) + add_rule_safe(world, LocationName.eternal_engine_2, player, + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.tails_bazooka, player)) + add_rule_safe(world, LocationName.crazy_gadget_2, player, + lambda state: state.has(ItemName.sonic_bounce_bracelet, player)) + + add_rule_safe(world, LocationName.lost_colony_2, player, + lambda state: state.has(ItemName.eggman_jet_engine, player)) + add_rule_safe(world, LocationName.weapons_bed_2, player, + lambda state: state.has(ItemName.eggman_jet_engine, player) and + state.has(ItemName.eggman_large_cannon, player)) + add_rule_safe(world, LocationName.security_hall_2, player, + lambda state: state.has(ItemName.rouge_pick_nails, player)) + add_rule_safe(world, LocationName.mad_space_2, player, + lambda state: state.has(ItemName.rouge_iron_boots, player)) + add_rule_safe(world, LocationName.cosmic_wall_2, player, + lambda state: state.has(ItemName.eggman_jet_engine, player)) + + add_rule_safe(world, LocationName.cannon_core_2, player, + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.eggman_jet_engine, player)) + + # Mission 3 Upgrade Requirements + add_rule_safe(world, LocationName.city_escape_3, player, + lambda state: state.has(ItemName.sonic_mystic_melody, player)) + add_rule_safe(world, LocationName.wild_canyon_3, player, + lambda state: state.has(ItemName.knuckles_shovel_claws, player) and + state.has(ItemName.knuckles_hammer_gloves, player) and + state.has(ItemName.knuckles_mystic_melody, player)) + add_rule_safe(world, LocationName.prison_lane_3, player, + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.tails_mystic_melody, player)) + add_rule_safe(world, LocationName.metal_harbor_3, player, + lambda state: state.has(ItemName.sonic_light_shoes, player) and + state.has(ItemName.sonic_bounce_bracelet, player) and + state.has(ItemName.sonic_mystic_melody, player)) + add_rule_safe(world, LocationName.green_forest_3, player, + lambda state: state.has(ItemName.sonic_bounce_bracelet, player) and + state.has(ItemName.sonic_mystic_melody, player)) + add_rule_safe(world, LocationName.pumpkin_hill_3, player, + lambda state: state.has(ItemName.knuckles_mystic_melody, player)) + add_rule_safe(world, LocationName.mission_street_3, player, + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.tails_mystic_melody, player)) + add_rule_safe(world, LocationName.aquatic_mine_3, player, + lambda state: state.has(ItemName.knuckles_mystic_melody, player)) + add_rule_safe(world, LocationName.hidden_base_3, player, + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.tails_mystic_melody, player)) + add_rule_safe(world, LocationName.pyramid_cave_3, player, + lambda state: state.has(ItemName.sonic_bounce_bracelet, player) and + state.has(ItemName.sonic_mystic_melody, player)) + add_rule_safe(world, LocationName.death_chamber_3, player, + lambda state: state.has(ItemName.knuckles_mystic_melody, player) and + state.has(ItemName.knuckles_air_necklace, player) and + state.has(ItemName.knuckles_hammer_gloves, player)) + add_rule_safe(world, LocationName.eternal_engine_3, player, + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.tails_mystic_melody, player)) + add_rule_safe(world, LocationName.meteor_herd_3, player, + lambda state: state.has(ItemName.knuckles_mystic_melody, player)) + add_rule_safe(world, LocationName.crazy_gadget_3, player, + lambda state: state.has(ItemName.sonic_light_shoes, player) and + state.has(ItemName.sonic_bounce_bracelet, player) and + state.has(ItemName.sonic_flame_ring, player) and + state.has(ItemName.sonic_mystic_melody, player)) + add_rule_safe(world, LocationName.final_rush_3, player, + lambda state: state.has(ItemName.sonic_light_shoes, player) and + state.has(ItemName.sonic_bounce_bracelet, player) and + state.has(ItemName.sonic_mystic_melody, player)) + + add_rule_safe(world, LocationName.iron_gate_3, player, + lambda state: state.has(ItemName.eggman_mystic_melody, player) and + state.has(ItemName.eggman_jet_engine, player) and + state.has(ItemName.eggman_large_cannon, player)) + add_rule_safe(world, LocationName.dry_lagoon_3, player, + lambda state: state.has(ItemName.rouge_mystic_melody, player) and + state.has(ItemName.rouge_pick_nails, player) and + state.has(ItemName.rouge_iron_boots, player)) + add_rule_safe(world, LocationName.sand_ocean_3, player, + lambda state: state.has(ItemName.eggman_jet_engine, player) and + state.has(ItemName.eggman_large_cannon, player)) + add_rule_safe(world, LocationName.radical_highway_3, player, + lambda state: state.has(ItemName.shadow_mystic_melody, player)) + add_rule_safe(world, LocationName.egg_quarters_3, player, + lambda state: state.has(ItemName.rouge_mystic_melody, player) and + state.has(ItemName.rouge_pick_nails, player) and + state.has(ItemName.rouge_iron_boots, player)) + add_rule_safe(world, LocationName.lost_colony_3, player, + lambda state: state.has(ItemName.eggman_mystic_melody, player) and + state.has(ItemName.eggman_jet_engine, player)) + add_rule_safe(world, LocationName.weapons_bed_3, player, + lambda state: state.has(ItemName.eggman_mystic_melody, player) and + state.has(ItemName.eggman_jet_engine, player) and + state.has(ItemName.eggman_large_cannon, player)) + add_rule_safe(world, LocationName.security_hall_3, player, + lambda state: state.has(ItemName.rouge_treasure_scope, player)) + add_rule_safe(world, LocationName.white_jungle_3, player, + lambda state: state.has(ItemName.shadow_air_shoes, player) and + state.has(ItemName.shadow_mystic_melody, player)) + add_rule_safe(world, LocationName.sky_rail_3, player, + lambda state: state.has(ItemName.shadow_air_shoes, player) and + state.has(ItemName.shadow_mystic_melody, player)) + add_rule_safe(world, LocationName.mad_space_3, player, + lambda state: state.has(ItemName.rouge_mystic_melody, player) and + state.has(ItemName.rouge_iron_boots, player)) + add_rule_safe(world, LocationName.cosmic_wall_3, player, + lambda state: state.has(ItemName.eggman_mystic_melody, player) and + state.has(ItemName.eggman_jet_engine, player)) + add_rule_safe(world, LocationName.final_chase_3, player, + lambda state: state.has(ItemName.shadow_air_shoes, player) and + state.has(ItemName.shadow_mystic_melody, player)) + + add_rule_safe(world, LocationName.cannon_core_3, player, + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.eggman_mystic_melody, player) and + state.has(ItemName.eggman_jet_engine, player) and + state.has(ItemName.eggman_large_cannon, player) and + state.has(ItemName.rouge_mystic_melody, player) and + state.has(ItemName.knuckles_mystic_melody, player) and + state.has(ItemName.knuckles_hammer_gloves, player) and + state.has(ItemName.knuckles_air_necklace, player) and + state.has(ItemName.sonic_bounce_bracelet, player) and + state.has(ItemName.sonic_light_shoes, player)) + + # Mission 4 Upgrade Requirements + add_rule_safe(world, LocationName.metal_harbor_4, player, + lambda state: state.has(ItemName.sonic_light_shoes, player)) + add_rule_safe(world, LocationName.pumpkin_hill_4, player, + lambda state: state.has(ItemName.knuckles_shovel_claws, player)) + add_rule_safe(world, LocationName.mission_street_4, player, + lambda state: state.has(ItemName.tails_booster, player)) + add_rule_safe(world, LocationName.aquatic_mine_4, player, + lambda state: state.has(ItemName.knuckles_shovel_claws, player)) + add_rule_safe(world, LocationName.hidden_base_4, player, + lambda state: state.has(ItemName.tails_booster, player)) + add_rule_safe(world, LocationName.pyramid_cave_4, player, + lambda state: state.has(ItemName.sonic_bounce_bracelet, player)) + add_rule_safe(world, LocationName.death_chamber_4, player, + lambda state: state.has(ItemName.knuckles_shovel_claws, player) and + state.has(ItemName.knuckles_hammer_gloves, player)) + add_rule_safe(world, LocationName.eternal_engine_4, player, + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.tails_bazooka, player)) + add_rule_safe(world, LocationName.meteor_herd_4, player, + lambda state: state.has(ItemName.knuckles_shovel_claws, player) and + state.has(ItemName.knuckles_hammer_gloves, player)) + add_rule_safe(world, LocationName.crazy_gadget_4, player, + lambda state: state.has(ItemName.sonic_light_shoes, player) and + state.has(ItemName.sonic_bounce_bracelet, player) and + state.has(ItemName.sonic_flame_ring, player)) + add_rule_safe(world, LocationName.final_rush_4, player, + lambda state: state.has(ItemName.sonic_bounce_bracelet, player)) + + add_rule_safe(world, LocationName.egg_quarters_4, player, + lambda state: state.has(ItemName.rouge_pick_nails, player)) + add_rule_safe(world, LocationName.lost_colony_4, player, + lambda state: state.has(ItemName.eggman_jet_engine, player)) + add_rule_safe(world, LocationName.weapons_bed_4, player, + lambda state: state.has(ItemName.eggman_jet_engine, player) and + state.has(ItemName.eggman_large_cannon, player)) + add_rule_safe(world, LocationName.security_hall_4, player, + lambda state: state.has(ItemName.rouge_pick_nails, player)) + add_rule_safe(world, LocationName.white_jungle_4, player, + lambda state: state.has(ItemName.shadow_air_shoes, player)) + add_rule_safe(world, LocationName.mad_space_4, player, + lambda state: state.has(ItemName.rouge_pick_nails, player) and + state.has(ItemName.rouge_iron_boots, player)) + add_rule_safe(world, LocationName.cosmic_wall_4, player, + lambda state: state.has(ItemName.eggman_jet_engine, player)) + + add_rule_safe(world, LocationName.cannon_core_4, player, + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.eggman_jet_engine, player) and + state.has(ItemName.knuckles_hammer_gloves, player) and + state.has(ItemName.knuckles_air_necklace, player) and + state.has(ItemName.sonic_bounce_bracelet, player)) + + # Mission 5 Upgrade Requirements + add_rule_safe(world, LocationName.city_escape_5, player, + lambda state: state.has(ItemName.sonic_flame_ring, player) and + state.has(ItemName.sonic_light_shoes, player)) + add_rule_safe(world, LocationName.wild_canyon_5, player, + lambda state: state.has(ItemName.knuckles_shovel_claws, player) and + state.has(ItemName.knuckles_sunglasses, player)) + add_rule_safe(world, LocationName.metal_harbor_5, player, + lambda state: state.has(ItemName.sonic_light_shoes, player)) + add_rule_safe(world, LocationName.green_forest_5, player, + lambda state: state.has(ItemName.sonic_bounce_bracelet, player)) + add_rule_safe(world, LocationName.pumpkin_hill_5, player, + lambda state: state.has(ItemName.knuckles_shovel_claws, player) and + state.has(ItemName.knuckles_sunglasses, player)) + add_rule_safe(world, LocationName.mission_street_5, player, + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.tails_bazooka, player)) + add_rule_safe(world, LocationName.aquatic_mine_5, player, + lambda state: state.has(ItemName.knuckles_mystic_melody, player) and + state.has(ItemName.knuckles_air_necklace, player) and + state.has(ItemName.knuckles_sunglasses, player)) + add_rule_safe(world, LocationName.hidden_base_5, player, + lambda state: state.has(ItemName.tails_booster, player)) + add_rule_safe(world, LocationName.pyramid_cave_5, player, + lambda state: state.has(ItemName.sonic_bounce_bracelet, player)) + add_rule_safe(world, LocationName.death_chamber_5, player, + lambda state: state.has(ItemName.knuckles_hammer_gloves, player) and + state.has(ItemName.knuckles_shovel_claws, player) and + state.has(ItemName.knuckles_mystic_melody, player) and + state.has(ItemName.knuckles_air_necklace, player)) + add_rule_safe(world, LocationName.eternal_engine_5, player, + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.tails_bazooka, player)) + add_rule_safe(world, LocationName.meteor_herd_5, player, + lambda state: state.has(ItemName.knuckles_sunglasses, player)) + add_rule_safe(world, LocationName.crazy_gadget_5, player, + lambda state: state.has(ItemName.sonic_light_shoes, player) and + state.has(ItemName.sonic_bounce_bracelet, player) and + state.has(ItemName.sonic_flame_ring, player)) + add_rule_safe(world, LocationName.final_rush_5, player, + lambda state: state.has(ItemName.sonic_bounce_bracelet, player)) + + add_rule_safe(world, LocationName.iron_gate_5, player, + lambda state: state.has(ItemName.eggman_large_cannon, player)) + add_rule_safe(world, LocationName.dry_lagoon_5, player, + lambda state: state.has(ItemName.rouge_treasure_scope, player)) + add_rule_safe(world, LocationName.sand_ocean_5, player, + lambda state: state.has(ItemName.eggman_jet_engine, player)) + add_rule_safe(world, LocationName.egg_quarters_5, player, + lambda state: state.has(ItemName.rouge_pick_nails, player) and + state.has(ItemName.rouge_treasure_scope, player)) + add_rule_safe(world, LocationName.lost_colony_5, player, + lambda state: state.has(ItemName.eggman_jet_engine, player) and + state.has(ItemName.eggman_large_cannon, player)) + add_rule_safe(world, LocationName.weapons_bed_5, player, + lambda state: state.has(ItemName.eggman_jet_engine, player) and + state.has(ItemName.eggman_large_cannon, player)) + add_rule_safe(world, LocationName.security_hall_5, player, + lambda state: state.has(ItemName.rouge_pick_nails, player) and + state.has(ItemName.rouge_treasure_scope, player) and + state.has(ItemName.rouge_iron_boots, player)) + add_rule_safe(world, LocationName.white_jungle_5, player, + lambda state: state.has(ItemName.shadow_air_shoes, player) and + state.has(ItemName.shadow_flame_ring, player)) + add_rule_safe(world, LocationName.mad_space_5, player, + lambda state: state.has(ItemName.rouge_iron_boots, player)) + add_rule_safe(world, LocationName.cosmic_wall_5, player, + lambda state: state.has(ItemName.eggman_jet_engine, player)) + + add_rule_safe(world, LocationName.cannon_core_5, player, + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.eggman_jet_engine, player) and + state.has(ItemName.knuckles_mystic_melody, player) and + state.has(ItemName.knuckles_hammer_gloves, player) and + state.has(ItemName.knuckles_air_necklace, player) and + state.has(ItemName.sonic_bounce_bracelet, player)) + + # Upgrade Spot Upgrade Requirements + add_rule(world.get_location(LocationName.city_escape_upgrade, player), + lambda state: state.has(ItemName.sonic_bounce_bracelet, player) and + state.has(ItemName.sonic_flame_ring, player)) + add_rule(world.get_location(LocationName.wild_canyon_upgrade, player), lambda state: state.has(ItemName.knuckles_shovel_claws, player)) - add_rule(world.get_location(LocationName.hidden_base_1, player), - lambda state: state.has(ItemName.tails_booster, player)) - add_rule(world.get_location(LocationName.pyramid_cave_1, player), - lambda state: state.has(ItemName.sonic_bounce_bracelet, player)) - add_rule(world.get_location(LocationName.death_chamber_1, player), - lambda state: state.has(ItemName.knuckles_shovel_claws, player) and - state.has(ItemName.knuckles_hammer_gloves, player)) - add_rule(world.get_location(LocationName.eternal_engine_1, player), + add_rule(world.get_location(LocationName.prison_lane_upgrade, player), + lambda state: state.has(ItemName.tails_bazooka, player)) + add_rule(world.get_location(LocationName.hidden_base_upgrade, player), lambda state: state.has(ItemName.tails_booster, player) and state.has(ItemName.tails_bazooka, player)) - add_rule(world.get_location(LocationName.meteor_herd_1, player), - lambda state: state.has(ItemName.knuckles_shovel_claws, player) and - state.has(ItemName.knuckles_hammer_gloves, player)) - add_rule(world.get_location(LocationName.crazy_gadget_1, player), - lambda state: state.has(ItemName.sonic_light_shoes, player) and - state.has(ItemName.sonic_bounce_bracelet, player) and - state.has(ItemName.sonic_flame_ring, player)) - add_rule(world.get_location(LocationName.final_rush_1, player), + add_rule(world.get_location(LocationName.eternal_engine_upgrade, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.meteor_herd_upgrade, player), + lambda state: state.has(ItemName.knuckles_hammer_gloves, player)) + add_rule(world.get_location(LocationName.crazy_gadget_upgrade, player), + lambda state: state.has(ItemName.sonic_bounce_bracelet, player)) + add_rule(world.get_location(LocationName.final_rush_upgrade, player), lambda state: state.has(ItemName.sonic_bounce_bracelet, player)) - add_rule(world.get_location(LocationName.egg_quarters_1, player), + add_rule(world.get_location(LocationName.iron_gate_upgrade, player), + lambda state: state.has(ItemName.eggman_large_cannon, player)) + add_rule(world.get_location(LocationName.dry_lagoon_upgrade, player), lambda state: state.has(ItemName.rouge_pick_nails, player)) - add_rule(world.get_location(LocationName.lost_colony_1, player), + add_rule(world.get_location(LocationName.sand_ocean_upgrade, player), lambda state: state.has(ItemName.eggman_jet_engine, player)) - add_rule(world.get_location(LocationName.weapons_bed_1, player), - lambda state: state.has(ItemName.eggman_jet_engine, player) and - state.has(ItemName.eggman_large_cannon, player)) - add_rule(world.get_location(LocationName.security_hall_1, player), - lambda state: state.has(ItemName.rouge_pick_nails, player)) - add_rule(world.get_location(LocationName.white_jungle_1, player), + add_rule(world.get_location(LocationName.radical_highway_upgrade, player), lambda state: state.has(ItemName.shadow_air_shoes, player)) - add_rule(world.get_location(LocationName.mad_space_1, player), - lambda state: state.has(ItemName.rouge_pick_nails, player) and + add_rule(world.get_location(LocationName.security_hall_upgrade, player), + lambda state: state.has(ItemName.rouge_mystic_melody, player) and state.has(ItemName.rouge_iron_boots, player)) - add_rule(world.get_location(LocationName.cosmic_wall_1, player), + add_rule(world.get_location(LocationName.cosmic_wall_upgrade, player), lambda state: state.has(ItemName.eggman_jet_engine, player)) - add_rule(world.get_location(LocationName.cannon_core_1, player), - lambda state: state.has(ItemName.tails_booster, player) and - state.has(ItemName.eggman_jet_engine, player) and - state.has(ItemName.knuckles_hammer_gloves, player) and - state.has(ItemName.knuckles_air_necklace, player) and - state.has(ItemName.sonic_bounce_bracelet, player)) + # Chao Key Upgrade Requirements + if world.keysanity[player]: + add_rule(world.get_location(LocationName.prison_lane_chao_1, player), + lambda state: state.has(ItemName.tails_bazooka, player)) + add_rule(world.get_location(LocationName.mission_street_chao_1, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.hidden_base_chao_1, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.death_chamber_chao_1, player), + lambda state: state.has(ItemName.knuckles_hammer_gloves, player)) + add_rule(world.get_location(LocationName.eternal_engine_chao_1, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.crazy_gadget_chao_1, player), + lambda state: state.has(ItemName.sonic_bounce_bracelet, player)) - # Mission 2 Upgrade Requirements - if world.include_missions[player].value >= 2: - add_rule(world.get_location(LocationName.metal_harbor_2, player), + add_rule(world.get_location(LocationName.cosmic_wall_chao_1, player), + lambda state: state.has(ItemName.eggman_mystic_melody, player) and + state.has(ItemName.eggman_jet_engine, player)) + + add_rule(world.get_location(LocationName.cannon_core_chao_1, player), + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.eggman_jet_engine, player) and + state.has(ItemName.eggman_large_cannon, player)) + + add_rule(world.get_location(LocationName.prison_lane_chao_2, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.metal_harbor_chao_2, player), lambda state: state.has(ItemName.sonic_light_shoes, player)) - add_rule(world.get_location(LocationName.mission_street_2, player), + add_rule(world.get_location(LocationName.mission_street_chao_2, player), lambda state: state.has(ItemName.tails_booster, player)) - add_rule(world.get_location(LocationName.hidden_base_2, player), + add_rule(world.get_location(LocationName.hidden_base_chao_2, player), lambda state: state.has(ItemName.tails_booster, player)) - add_rule(world.get_location(LocationName.death_chamber_2, player), - lambda state: state.has(ItemName.knuckles_shovel_claws, player) and - state.has(ItemName.knuckles_hammer_gloves, player)) - add_rule(world.get_location(LocationName.eternal_engine_2, player), + add_rule(world.get_location(LocationName.pyramid_cave_chao_2, player), + lambda state: state.has(ItemName.sonic_bounce_bracelet, player)) + add_rule(world.get_location(LocationName.death_chamber_chao_2, player), + lambda state: state.has(ItemName.knuckles_hammer_gloves, player)) + add_rule(world.get_location(LocationName.eternal_engine_chao_2, player), lambda state: state.has(ItemName.tails_booster, player) and state.has(ItemName.tails_bazooka, player)) - add_rule(world.get_location(LocationName.crazy_gadget_2, player), + add_rule(world.get_location(LocationName.crazy_gadget_chao_2, player), lambda state: state.has(ItemName.sonic_bounce_bracelet, player)) - add_rule(world.get_location(LocationName.lost_colony_2, player), - lambda state: state.has(ItemName.eggman_jet_engine, player)) - add_rule(world.get_location(LocationName.weapons_bed_2, player), + add_rule(world.get_location(LocationName.weapons_bed_chao_2, player), lambda state: state.has(ItemName.eggman_jet_engine, player) and state.has(ItemName.eggman_large_cannon, player)) - add_rule(world.get_location(LocationName.security_hall_2, player), - lambda state: state.has(ItemName.rouge_pick_nails, player)) - add_rule(world.get_location(LocationName.mad_space_2, player), + add_rule(world.get_location(LocationName.white_jungle_chao_2, player), + lambda state: state.has(ItemName.shadow_air_shoes, player)) + add_rule(world.get_location(LocationName.mad_space_chao_2, player), lambda state: state.has(ItemName.rouge_iron_boots, player)) - add_rule(world.get_location(LocationName.cosmic_wall_2, player), + add_rule(world.get_location(LocationName.cosmic_wall_chao_2, player), lambda state: state.has(ItemName.eggman_jet_engine, player)) - add_rule(world.get_location(LocationName.cannon_core_2, player), + add_rule(world.get_location(LocationName.cannon_core_chao_2, player), lambda state: state.has(ItemName.tails_booster, player) and state.has(ItemName.eggman_jet_engine, player)) - # Mission 3 Upgrade Requirements - if world.include_missions[player].value >= 3: - add_rule(world.get_location(LocationName.city_escape_3, player), - lambda state: state.has(ItemName.sonic_mystic_melody, player)) - add_rule(world.get_location(LocationName.wild_canyon_3, player), - lambda state: state.has(ItemName.knuckles_shovel_claws, player) and - state.has(ItemName.knuckles_hammer_gloves, player) and - state.has(ItemName.knuckles_mystic_melody, player)) - add_rule(world.get_location(LocationName.prison_lane_3, player), - lambda state: state.has(ItemName.tails_booster, player) and - state.has(ItemName.tails_mystic_melody, player)) - add_rule(world.get_location(LocationName.metal_harbor_3, player), + add_rule(world.get_location(LocationName.metal_harbor_chao_3, player), + lambda state: state.has(ItemName.sonic_light_shoes, player)) + add_rule(world.get_location(LocationName.mission_street_chao_3, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.pyramid_cave_chao_3, player), lambda state: state.has(ItemName.sonic_light_shoes, player) and state.has(ItemName.sonic_bounce_bracelet, player) and state.has(ItemName.sonic_mystic_melody, player)) - add_rule(world.get_location(LocationName.green_forest_3, player), - lambda state: state.has(ItemName.sonic_bounce_bracelet, player) and - state.has(ItemName.sonic_mystic_melody, player)) - add_rule(world.get_location(LocationName.pumpkin_hill_3, player), - lambda state: state.has(ItemName.knuckles_mystic_melody, player)) - add_rule(world.get_location(LocationName.mission_street_3, player), - lambda state: state.has(ItemName.tails_booster, player) and - state.has(ItemName.tails_mystic_melody, player)) - add_rule(world.get_location(LocationName.aquatic_mine_3, player), - lambda state: state.has(ItemName.knuckles_mystic_melody, player)) - add_rule(world.get_location(LocationName.hidden_base_3, player), - lambda state: state.has(ItemName.tails_booster, player) and - state.has(ItemName.tails_mystic_melody, player)) - add_rule(world.get_location(LocationName.pyramid_cave_3, player), - lambda state: state.has(ItemName.sonic_bounce_bracelet, player) and - state.has(ItemName.sonic_mystic_melody, player)) - add_rule(world.get_location(LocationName.death_chamber_3, player), - lambda state: state.has(ItemName.knuckles_mystic_melody, player) and - state.has(ItemName.knuckles_air_necklace, player) and + add_rule(world.get_location(LocationName.death_chamber_chao_3, player), + lambda state: state.has(ItemName.knuckles_shovel_claws, player) and state.has(ItemName.knuckles_hammer_gloves, player)) - add_rule(world.get_location(LocationName.eternal_engine_3, player), - lambda state: state.has(ItemName.tails_booster, player) and - state.has(ItemName.tails_mystic_melody, player)) - add_rule(world.get_location(LocationName.meteor_herd_3, player), - lambda state: state.has(ItemName.knuckles_mystic_melody, player)) - add_rule(world.get_location(LocationName.crazy_gadget_3, player), - lambda state: state.has(ItemName.sonic_light_shoes, player) and - state.has(ItemName.sonic_bounce_bracelet, player) and - state.has(ItemName.sonic_flame_ring, player) and - state.has(ItemName.sonic_mystic_melody, player)) - add_rule(world.get_location(LocationName.final_rush_3, player), + add_rule(world.get_location(LocationName.eternal_engine_chao_3, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.crazy_gadget_chao_3, player), lambda state: state.has(ItemName.sonic_light_shoes, player) and state.has(ItemName.sonic_bounce_bracelet, player) and - state.has(ItemName.sonic_mystic_melody, player)) + state.has(ItemName.sonic_flame_ring, player)) + add_rule(world.get_location(LocationName.final_rush_chao_3, player), + lambda state: state.has(ItemName.sonic_bounce_bracelet, player)) - add_rule(world.get_location(LocationName.iron_gate_3, player), - lambda state: state.has(ItemName.eggman_mystic_melody, player) and - state.has(ItemName.eggman_jet_engine, player) and - state.has(ItemName.eggman_large_cannon, player)) - add_rule(world.get_location(LocationName.dry_lagoon_3, player), - lambda state: state.has(ItemName.rouge_mystic_melody, player) and - state.has(ItemName.rouge_pick_nails, player) and - state.has(ItemName.rouge_iron_boots, player)) - add_rule(world.get_location(LocationName.sand_ocean_3, player), + add_rule(world.get_location(LocationName.egg_quarters_chao_3, player), + lambda state: state.has(ItemName.rouge_mystic_melody, player)) + add_rule(world.get_location(LocationName.lost_colony_chao_3, player), + lambda state: state.has(ItemName.eggman_jet_engine, player)) + add_rule(world.get_location(LocationName.weapons_bed_chao_3, player), lambda state: state.has(ItemName.eggman_jet_engine, player) and state.has(ItemName.eggman_large_cannon, player)) - add_rule(world.get_location(LocationName.radical_highway_3, player), - lambda state: state.has(ItemName.shadow_mystic_melody, player)) - add_rule(world.get_location(LocationName.egg_quarters_3, player), - lambda state: state.has(ItemName.rouge_mystic_melody, player) and - state.has(ItemName.rouge_pick_nails, player) and - state.has(ItemName.rouge_iron_boots, player)) - add_rule(world.get_location(LocationName.lost_colony_3, player), - lambda state: state.has(ItemName.eggman_mystic_melody, player) and - state.has(ItemName.eggman_jet_engine, player)) - add_rule(world.get_location(LocationName.weapons_bed_3, player), - lambda state: state.has(ItemName.eggman_mystic_melody, player) and - state.has(ItemName.eggman_jet_engine, player) and - state.has(ItemName.eggman_large_cannon, player)) - add_rule(world.get_location(LocationName.security_hall_3, player), - lambda state: state.has(ItemName.rouge_treasure_scope, player)) - add_rule(world.get_location(LocationName.white_jungle_3, player), - lambda state: state.has(ItemName.shadow_air_shoes, player) and - state.has(ItemName.shadow_mystic_melody, player)) - add_rule(world.get_location(LocationName.sky_rail_3, player), - lambda state: state.has(ItemName.shadow_air_shoes, player) and - state.has(ItemName.shadow_mystic_melody, player)) - add_rule(world.get_location(LocationName.mad_space_3, player), - lambda state: state.has(ItemName.rouge_mystic_melody, player) and - state.has(ItemName.rouge_iron_boots, player)) - add_rule(world.get_location(LocationName.cosmic_wall_3, player), - lambda state: state.has(ItemName.eggman_mystic_melody, player) and - state.has(ItemName.eggman_jet_engine, player)) - add_rule(world.get_location(LocationName.final_chase_3, player), - lambda state: state.has(ItemName.shadow_air_shoes, player) and - state.has(ItemName.shadow_mystic_melody, player)) + add_rule(world.get_location(LocationName.security_hall_chao_3, player), + lambda state: state.has(ItemName.rouge_pick_nails, player)) + add_rule(world.get_location(LocationName.white_jungle_chao_3, player), + lambda state: state.has(ItemName.shadow_air_shoes, player)) + add_rule(world.get_location(LocationName.mad_space_chao_3, player), + lambda state: state.has(ItemName.rouge_iron_boots, player)) + add_rule(world.get_location(LocationName.cosmic_wall_chao_3, player), + lambda state: state.has(ItemName.eggman_jet_engine, player)) - add_rule(world.get_location(LocationName.cannon_core_3, player), + add_rule(world.get_location(LocationName.cannon_core_chao_3, player), lambda state: state.has(ItemName.tails_booster, player) and - state.has(ItemName.eggman_mystic_melody, player) and state.has(ItemName.eggman_jet_engine, player) and - state.has(ItemName.eggman_large_cannon, player) and - state.has(ItemName.rouge_mystic_melody, player) and - state.has(ItemName.knuckles_mystic_melody, player) and state.has(ItemName.knuckles_hammer_gloves, player) and state.has(ItemName.knuckles_air_necklace, player) and - state.has(ItemName.sonic_bounce_bracelet, player) and - state.has(ItemName.sonic_light_shoes, player)) + state.has(ItemName.sonic_flame_ring, player)) - # Mission 4 Upgrade Requirements - if world.include_missions[player].value >= 4: - add_rule(world.get_location(LocationName.metal_harbor_4, player), - lambda state: state.has(ItemName.sonic_light_shoes, player)) - add_rule(world.get_location(LocationName.pumpkin_hill_4, player), - lambda state: state.has(ItemName.knuckles_shovel_claws, player)) - add_rule(world.get_location(LocationName.mission_street_4, player), + # Pipe Upgrade Requirements + if world.whistlesanity[player].value == 1 or world.whistlesanity[player].value == 3: + add_rule(world.get_location(LocationName.mission_street_pipe_1, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.hidden_base_pipe_1, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.eternal_engine_pipe_1, player), + lambda state: state.has(ItemName.tails_booster, player)) + + add_rule(world.get_location(LocationName.sand_ocean_pipe_1, player), + lambda state: state.has(ItemName.eggman_jet_engine, player)) + add_rule(world.get_location(LocationName.cosmic_wall_pipe_1, player), + lambda state: state.has(ItemName.eggman_jet_engine, player)) + + add_rule(world.get_location(LocationName.mission_street_pipe_2, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.hidden_base_pipe_2, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.death_chamber_pipe_2, player), + lambda state: state.has(ItemName.knuckles_shovel_claws, player) and + state.has(ItemName.knuckles_hammer_gloves, player)) + add_rule(world.get_location(LocationName.eternal_engine_pipe_2, player), lambda state: state.has(ItemName.tails_booster, player)) - add_rule(world.get_location(LocationName.aquatic_mine_4, player), - lambda state: state.has(ItemName.knuckles_shovel_claws, player)) - add_rule(world.get_location(LocationName.hidden_base_4, player), + add_rule(world.get_location(LocationName.crazy_gadget_pipe_2, player), + lambda state: state.has(ItemName.sonic_bounce_bracelet, player)) + + add_rule(world.get_location(LocationName.sand_ocean_pipe_2, player), + lambda state: state.has(ItemName.eggman_jet_engine, player)) + add_rule(world.get_location(LocationName.lost_colony_pipe_2, player), + lambda state: state.has(ItemName.eggman_jet_engine, player)) + add_rule(world.get_location(LocationName.cosmic_wall_pipe_2, player), + lambda state: state.has(ItemName.eggman_jet_engine, player)) + + add_rule(world.get_location(LocationName.cannon_core_pipe_2, player), lambda state: state.has(ItemName.tails_booster, player)) - add_rule(world.get_location(LocationName.pyramid_cave_4, player), + + add_rule(world.get_location(LocationName.prison_lane_pipe_3, player), + lambda state: state.has(ItemName.tails_bazooka, player)) + add_rule(world.get_location(LocationName.mission_street_pipe_3, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.hidden_base_pipe_3, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.pyramid_cave_pipe_3, player), lambda state: state.has(ItemName.sonic_bounce_bracelet, player)) - add_rule(world.get_location(LocationName.death_chamber_4, player), + add_rule(world.get_location(LocationName.death_chamber_pipe_3, player), lambda state: state.has(ItemName.knuckles_shovel_claws, player) and state.has(ItemName.knuckles_hammer_gloves, player)) - add_rule(world.get_location(LocationName.eternal_engine_4, player), + add_rule(world.get_location(LocationName.eternal_engine_pipe_3, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.crazy_gadget_pipe_3, player), + lambda state: state.has(ItemName.sonic_bounce_bracelet, player) and + state.has(ItemName.sonic_mystic_melody, player)) + + add_rule(world.get_location(LocationName.weapons_bed_pipe_3, player), + lambda state: state.has(ItemName.eggman_jet_engine, player) and + state.has(ItemName.eggman_large_cannon, player)) + add_rule(world.get_location(LocationName.white_jungle_pipe_3, player), + lambda state: state.has(ItemName.shadow_air_shoes, player)) + add_rule(world.get_location(LocationName.mad_space_pipe_3, player), + lambda state: state.has(ItemName.rouge_iron_boots, player)) + add_rule(world.get_location(LocationName.cosmic_wall_pipe_3, player), + lambda state: state.has(ItemName.eggman_jet_engine, player)) + + add_rule(world.get_location(LocationName.cannon_core_pipe_3, player), + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.eggman_jet_engine, player)) + + add_rule(world.get_location(LocationName.hidden_base_pipe_4, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.pyramid_cave_pipe_4, player), + lambda state: state.has(ItemName.sonic_bounce_bracelet, player)) + add_rule(world.get_location(LocationName.eternal_engine_pipe_4, player), lambda state: state.has(ItemName.tails_booster, player) and state.has(ItemName.tails_bazooka, player)) - add_rule(world.get_location(LocationName.meteor_herd_4, player), - lambda state: state.has(ItemName.knuckles_shovel_claws, player) and - state.has(ItemName.knuckles_hammer_gloves, player)) - add_rule(world.get_location(LocationName.crazy_gadget_4, player), + add_rule(world.get_location(LocationName.crazy_gadget_pipe_4, player), lambda state: state.has(ItemName.sonic_light_shoes, player) and state.has(ItemName.sonic_bounce_bracelet, player) and state.has(ItemName.sonic_flame_ring, player)) - add_rule(world.get_location(LocationName.final_rush_4, player), - lambda state: state.has(ItemName.sonic_bounce_bracelet, player)) - add_rule(world.get_location(LocationName.egg_quarters_4, player), - lambda state: state.has(ItemName.rouge_pick_nails, player)) - add_rule(world.get_location(LocationName.lost_colony_4, player), - lambda state: state.has(ItemName.eggman_jet_engine, player)) - add_rule(world.get_location(LocationName.weapons_bed_4, player), + add_rule(world.get_location(LocationName.weapons_bed_pipe_4, player), lambda state: state.has(ItemName.eggman_jet_engine, player) and state.has(ItemName.eggman_large_cannon, player)) - add_rule(world.get_location(LocationName.security_hall_4, player), - lambda state: state.has(ItemName.rouge_pick_nails, player)) - add_rule(world.get_location(LocationName.white_jungle_4, player), + add_rule(world.get_location(LocationName.white_jungle_pipe_4, player), lambda state: state.has(ItemName.shadow_air_shoes, player)) - add_rule(world.get_location(LocationName.mad_space_4, player), - lambda state: state.has(ItemName.rouge_pick_nails, player) and - state.has(ItemName.rouge_iron_boots, player)) - add_rule(world.get_location(LocationName.cosmic_wall_4, player), + add_rule(world.get_location(LocationName.mad_space_pipe_4, player), + lambda state: state.has(ItemName.rouge_iron_boots, player)) + add_rule(world.get_location(LocationName.cosmic_wall_pipe_4, player), + lambda state: state.has(ItemName.eggman_jet_engine, player)) + + add_rule(world.get_location(LocationName.cannon_core_pipe_4, player), + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.eggman_jet_engine, player)) + + add_rule(world.get_location(LocationName.hidden_base_pipe_5, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.eternal_engine_pipe_5, player), + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.tails_bazooka, player)) + + add_rule(world.get_location(LocationName.weapons_bed_pipe_5, player), + lambda state: state.has(ItemName.eggman_jet_engine, player) and + state.has(ItemName.eggman_large_cannon, player)) + add_rule(world.get_location(LocationName.cosmic_wall_pipe_5, player), lambda state: state.has(ItemName.eggman_jet_engine, player)) - add_rule(world.get_location(LocationName.cannon_core_4, player), + add_rule(world.get_location(LocationName.cannon_core_pipe_5, player), lambda state: state.has(ItemName.tails_booster, player) and state.has(ItemName.eggman_jet_engine, player) and state.has(ItemName.knuckles_hammer_gloves, player) and - state.has(ItemName.knuckles_air_necklace, player) and - state.has(ItemName.sonic_bounce_bracelet, player)) + state.has(ItemName.knuckles_air_necklace, player)) - # Mission 5 Upgrade Requirements - if world.include_missions[player].value >= 5: - add_rule(world.get_location(LocationName.city_escape_5, player), - lambda state: state.has(ItemName.sonic_flame_ring, player) and - state.has(ItemName.sonic_light_shoes, player)) - add_rule(world.get_location(LocationName.wild_canyon_5, player), + # Hidden Whistle Upgrade Requirements + if world.whistlesanity[player].value == 2 or world.whistlesanity[player].value == 3: + add_rule(world.get_location(LocationName.mission_street_hidden_3, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.death_chamber_hidden_1, player), lambda state: state.has(ItemName.knuckles_shovel_claws, player) and - state.has(ItemName.knuckles_sunglasses, player)) - add_rule(world.get_location(LocationName.metal_harbor_5, player), + state.has(ItemName.knuckles_hammer_gloves, player)) + add_rule(world.get_location(LocationName.death_chamber_hidden_2, player), + lambda state: state.has(ItemName.knuckles_hammer_gloves, player)) + add_rule(world.get_location(LocationName.crazy_gadget_hidden_1, player), lambda state: state.has(ItemName.sonic_light_shoes, player)) - add_rule(world.get_location(LocationName.pumpkin_hill_5, player), - lambda state: state.has(ItemName.knuckles_shovel_claws, player) and - state.has(ItemName.knuckles_sunglasses, player)) - add_rule(world.get_location(LocationName.mission_street_5, player), + add_rule(world.get_location(LocationName.white_jungle_hidden_3, player), + lambda state: state.has(ItemName.shadow_air_shoes, player)) + add_rule(world.get_location(LocationName.cannon_core_hidden_1, player), lambda state: state.has(ItemName.tails_booster, player) and - state.has(ItemName.tails_bazooka, player)) - add_rule(world.get_location(LocationName.aquatic_mine_5, player), - lambda state: state.has(ItemName.knuckles_mystic_melody, player) and - state.has(ItemName.knuckles_air_necklace, player) and - state.has(ItemName.knuckles_sunglasses, player)) - add_rule(world.get_location(LocationName.hidden_base_5, player), + state.has(ItemName.eggman_jet_engine, player)) + + # Gold Beetle Upgrade Requirements + if world.beetlesanity[player]: + add_rule(world.get_location(LocationName.mission_street_beetle, player), lambda state: state.has(ItemName.tails_booster, player)) - add_rule(world.get_location(LocationName.pyramid_cave_5, player), + add_rule(world.get_location(LocationName.hidden_base_beetle, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.pyramid_cave_beetle, player), lambda state: state.has(ItemName.sonic_bounce_bracelet, player)) - add_rule(world.get_location(LocationName.death_chamber_5, player), - lambda state: state.has(ItemName.knuckles_hammer_gloves, player) and - state.has(ItemName.knuckles_shovel_claws, player) and - state.has(ItemName.knuckles_mystic_melody, player) and - state.has(ItemName.knuckles_air_necklace, player)) - add_rule(world.get_location(LocationName.eternal_engine_5, player), + add_rule(world.get_location(LocationName.death_chamber_beetle, player), + lambda state: state.has(ItemName.knuckles_shovel_claws, player) and + state.has(ItemName.knuckles_hammer_gloves, player)) + add_rule(world.get_location(LocationName.eternal_engine_beetle, player), lambda state: state.has(ItemName.tails_booster, player) and state.has(ItemName.tails_bazooka, player)) - add_rule(world.get_location(LocationName.meteor_herd_5, player), - lambda state: state.has(ItemName.knuckles_hammer_gloves, player) and - state.has(ItemName.knuckles_sunglasses, player)) - add_rule(world.get_location(LocationName.crazy_gadget_5, player), + add_rule(world.get_location(LocationName.crazy_gadget_beetle, player), lambda state: state.has(ItemName.sonic_light_shoes, player) and state.has(ItemName.sonic_bounce_bracelet, player) and state.has(ItemName.sonic_flame_ring, player)) - add_rule(world.get_location(LocationName.final_rush_5, player), - lambda state: state.has(ItemName.sonic_bounce_bracelet, player)) - add_rule(world.get_location(LocationName.iron_gate_5, player), - lambda state: state.has(ItemName.eggman_large_cannon, player)) - add_rule(world.get_location(LocationName.dry_lagoon_5, player), - lambda state: state.has(ItemName.rouge_treasure_scope, player)) - add_rule(world.get_location(LocationName.egg_quarters_5, player), - lambda state: state.has(ItemName.rouge_treasure_scope, player)) - add_rule(world.get_location(LocationName.lost_colony_5, player), - lambda state: state.has(ItemName.eggman_jet_engine, player) and - state.has(ItemName.eggman_large_cannon, player)) - add_rule(world.get_location(LocationName.weapons_bed_5, player), - lambda state: state.has(ItemName.eggman_jet_engine, player) and - state.has(ItemName.eggman_large_cannon, player)) - add_rule(world.get_location(LocationName.security_hall_5, player), - lambda state: state.has(ItemName.rouge_pick_nails, player) and - state.has(ItemName.rouge_treasure_scope, player) and + add_rule(world.get_location(LocationName.dry_lagoon_beetle, player), + lambda state: state.has(ItemName.rouge_mystic_melody, player) and + state.has(ItemName.rouge_pick_nails, player) and state.has(ItemName.rouge_iron_boots, player)) - add_rule(world.get_location(LocationName.white_jungle_5, player), - lambda state: state.has(ItemName.shadow_air_shoes, player) and - state.has(ItemName.shadow_flame_ring, player)) - add_rule(world.get_location(LocationName.cosmic_wall_5, player), + add_rule(world.get_location(LocationName.lost_colony_beetle, player), lambda state: state.has(ItemName.eggman_jet_engine, player)) + add_rule(world.get_location(LocationName.white_jungle_beetle, player), + lambda state: state.has(ItemName.shadow_air_shoes, player)) + add_rule(world.get_location(LocationName.mad_space_beetle, player), + lambda state: state.has(ItemName.rouge_mystic_melody, player) and + state.has(ItemName.rouge_iron_boots, player)) + add_rule(world.get_location(LocationName.cosmic_wall_beetle, player), + lambda state: state.has(ItemName.eggman_mystic_melody, player) and + state.has(ItemName.eggman_jet_engine, player)) - add_rule(world.get_location(LocationName.cannon_core_5, player), + add_rule(world.get_location(LocationName.cannon_core_beetle, player), lambda state: state.has(ItemName.tails_booster, player) and state.has(ItemName.eggman_jet_engine, player) and - state.has(ItemName.knuckles_mystic_melody, player) and state.has(ItemName.knuckles_hammer_gloves, player) and - state.has(ItemName.knuckles_air_necklace, player) and - state.has(ItemName.sonic_bounce_bracelet, player)) + state.has(ItemName.knuckles_air_necklace, player)) + +def set_mission_upgrade_rules_hard(world: MultiWorld, player: int): + # Mission 1 Upgrade Requirements + add_rule_safe(world, LocationName.pumpkin_hill_1, player, + lambda state: state.has(ItemName.knuckles_shovel_claws, player)) + add_rule_safe(world, LocationName.mission_street_1, player, + lambda state: state.has(ItemName.tails_booster, player)) + add_rule_safe(world, LocationName.hidden_base_1, player, + lambda state: state.has(ItemName.tails_booster, player)) + add_rule_safe(world, LocationName.death_chamber_1, player, + lambda state: state.has(ItemName.knuckles_shovel_claws, player) and + state.has(ItemName.knuckles_hammer_gloves, player)) + add_rule_safe(world, LocationName.eternal_engine_1, player, + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.tails_bazooka, player)) + add_rule_safe(world, LocationName.crazy_gadget_1, player, + lambda state: state.has(ItemName.sonic_light_shoes, player) and + state.has(ItemName.sonic_flame_ring, player)) + add_rule_safe(world, LocationName.final_rush_1, player, + lambda state: state.has(ItemName.sonic_bounce_bracelet, player)) + + add_rule_safe(world, LocationName.egg_quarters_1, player, + lambda state: state.has(ItemName.rouge_pick_nails, player)) + add_rule_safe(world, LocationName.lost_colony_1, player, + lambda state: state.has(ItemName.eggman_jet_engine, player)) + add_rule_safe(world, LocationName.weapons_bed_1, player, + lambda state: state.has(ItemName.eggman_jet_engine, player)) + add_rule_safe(world, LocationName.cosmic_wall_1, player, + lambda state: state.has(ItemName.eggman_jet_engine, player)) + + add_rule_safe(world, LocationName.cannon_core_1, player, + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.knuckles_hammer_gloves, player)) + + # Mission 2 Upgrade Requirements + add_rule_safe(world, LocationName.mission_street_2, player, + lambda state: state.has(ItemName.tails_booster, player)) + add_rule_safe(world, LocationName.hidden_base_2, player, + lambda state: state.has(ItemName.tails_booster, player)) + add_rule_safe(world, LocationName.death_chamber_2, player, + lambda state: state.has(ItemName.knuckles_hammer_gloves, player)) + add_rule_safe(world, LocationName.eternal_engine_2, player, + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.tails_bazooka, player)) + + add_rule_safe(world, LocationName.lost_colony_2, player, + lambda state: state.has(ItemName.eggman_jet_engine, player)) + add_rule_safe(world, LocationName.weapons_bed_2, player, + lambda state: state.has(ItemName.eggman_jet_engine, player) and + state.has(ItemName.eggman_large_cannon, player)) + add_rule_safe(world, LocationName.cosmic_wall_2, player, + lambda state: state.has(ItemName.eggman_jet_engine, player)) + + add_rule_safe(world, LocationName.cannon_core_2, player, + lambda state: state.has(ItemName.tails_booster, player)) + + # Mission 3 Upgrade Requirements + add_rule_safe(world, LocationName.city_escape_3, player, + lambda state: state.has(ItemName.sonic_bounce_bracelet, player) or + state.has(ItemName.sonic_mystic_melody, player)) + add_rule_safe(world, LocationName.wild_canyon_3, player, + lambda state: state.has(ItemName.knuckles_shovel_claws, player)) + add_rule_safe(world, LocationName.prison_lane_3, player, + lambda state: state.has(ItemName.tails_booster, player)) + add_rule_safe(world, LocationName.mission_street_3, player, + lambda state: state.has(ItemName.tails_booster, player)) + add_rule_safe(world, LocationName.aquatic_mine_3, player, + lambda state: state.has(ItemName.knuckles_mystic_melody, player)) + add_rule_safe(world, LocationName.hidden_base_3, player, + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.tails_mystic_melody, player)) + add_rule_safe(world, LocationName.death_chamber_3, player, + lambda state: state.has(ItemName.knuckles_mystic_melody, player) and + state.has(ItemName.knuckles_hammer_gloves, player)) + add_rule_safe(world, LocationName.eternal_engine_3, player, + lambda state: state.has(ItemName.tails_booster, player)) + add_rule_safe(world, LocationName.meteor_herd_3, player, + lambda state: state.has(ItemName.knuckles_mystic_melody, player)) + add_rule_safe(world, LocationName.crazy_gadget_3, player, + lambda state: state.has(ItemName.sonic_light_shoes, player) and + state.has(ItemName.sonic_flame_ring, player)) + add_rule_safe(world, LocationName.final_rush_3, player, + lambda state: state.has(ItemName.sonic_bounce_bracelet, player)) + + add_rule_safe(world, LocationName.iron_gate_3, player, + lambda state: state.has(ItemName.eggman_mystic_melody, player) and + state.has(ItemName.eggman_jet_engine, player)) + add_rule_safe(world, LocationName.dry_lagoon_3, player, + lambda state: state.has(ItemName.rouge_mystic_melody, player) and + state.has(ItemName.rouge_pick_nails, player) and + state.has(ItemName.rouge_iron_boots, player)) + add_rule_safe(world, LocationName.sand_ocean_3, player, + lambda state: state.has(ItemName.eggman_jet_engine, player)) + add_rule_safe(world, LocationName.egg_quarters_3, player, + lambda state: state.has(ItemName.rouge_mystic_melody, player) and + state.has(ItemName.rouge_pick_nails, player) and + state.has(ItemName.rouge_iron_boots, player)) + add_rule_safe(world, LocationName.lost_colony_3, player, + lambda state: state.has(ItemName.eggman_mystic_melody, player) and + state.has(ItemName.eggman_jet_engine, player)) + add_rule_safe(world, LocationName.weapons_bed_3, player, + lambda state: state.has(ItemName.eggman_jet_engine, player)) + add_rule_safe(world, LocationName.mad_space_3, player, + lambda state: state.has(ItemName.rouge_mystic_melody, player) and + state.has(ItemName.rouge_iron_boots, player)) + add_rule_safe(world, LocationName.cosmic_wall_3, player, + lambda state: state.has(ItemName.eggman_jet_engine, player)) + + add_rule_safe(world, LocationName.cannon_core_3, player, + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.knuckles_hammer_gloves, player)) + + # Mission 4 Upgrade Requirements + add_rule_safe(world, LocationName.pumpkin_hill_4, player, + lambda state: state.has(ItemName.knuckles_shovel_claws, player)) + add_rule_safe(world, LocationName.mission_street_4, player, + lambda state: state.has(ItemName.tails_booster, player)) + add_rule_safe(world, LocationName.hidden_base_4, player, + lambda state: state.has(ItemName.tails_booster, player)) + add_rule_safe(world, LocationName.death_chamber_4, player, + lambda state: state.has(ItemName.knuckles_shovel_claws, player) and + state.has(ItemName.knuckles_hammer_gloves, player)) + add_rule_safe(world, LocationName.eternal_engine_4, player, + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.tails_bazooka, player)) + add_rule_safe(world, LocationName.crazy_gadget_4, player, + lambda state: state.has(ItemName.sonic_light_shoes, player) and + state.has(ItemName.sonic_flame_ring, player)) + add_rule_safe(world, LocationName.final_rush_4, player, + lambda state: state.has(ItemName.sonic_bounce_bracelet, player)) + add_rule_safe(world, LocationName.egg_quarters_4, player, + lambda state: state.has(ItemName.rouge_pick_nails, player)) + add_rule_safe(world, LocationName.lost_colony_4, player, + lambda state: state.has(ItemName.eggman_jet_engine, player)) + add_rule_safe(world, LocationName.weapons_bed_4, player, + lambda state: state.has(ItemName.eggman_jet_engine, player)) + add_rule_safe(world, LocationName.cosmic_wall_4, player, + lambda state: state.has(ItemName.eggman_jet_engine, player)) + + add_rule_safe(world, LocationName.cannon_core_4, player, + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.knuckles_hammer_gloves, player)) + + # Mission 5 Upgrade Requirements + add_rule_safe(world, LocationName.city_escape_5, player, + lambda state: state.has(ItemName.sonic_flame_ring, player)) + add_rule_safe(world, LocationName.wild_canyon_5, player, + lambda state: state.has(ItemName.knuckles_shovel_claws, player)) + add_rule_safe(world, LocationName.pumpkin_hill_5, player, + lambda state: state.has(ItemName.knuckles_shovel_claws, player)) + add_rule_safe(world, LocationName.mission_street_5, player, + lambda state: state.has(ItemName.tails_booster, player)) + add_rule_safe(world, LocationName.aquatic_mine_5, player, + lambda state: state.has(ItemName.knuckles_mystic_melody, player)) + add_rule_safe(world, LocationName.hidden_base_5, player, + lambda state: state.has(ItemName.tails_booster, player)) + add_rule_safe(world, LocationName.death_chamber_5, player, + lambda state: state.has(ItemName.knuckles_hammer_gloves, player) and + state.has(ItemName.knuckles_shovel_claws, player) and + state.has(ItemName.knuckles_mystic_melody, player)) + add_rule_safe(world, LocationName.eternal_engine_5, player, + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.tails_bazooka, player)) + add_rule_safe(world, LocationName.crazy_gadget_5, player, + lambda state: state.has(ItemName.sonic_light_shoes, player) and + state.has(ItemName.sonic_bounce_bracelet, player) and + state.has(ItemName.sonic_flame_ring, player)) + add_rule_safe(world, LocationName.final_rush_5, player, + lambda state: state.has(ItemName.sonic_bounce_bracelet, player)) + + add_rule_safe(world, LocationName.iron_gate_5, player, + lambda state: state.has(ItemName.eggman_large_cannon, player)) + add_rule_safe(world, LocationName.dry_lagoon_5, player, + lambda state: state.has(ItemName.rouge_treasure_scope, player)) + add_rule_safe(world, LocationName.sand_ocean_5, player, + lambda state: state.has(ItemName.eggman_jet_engine, player)) + add_rule_safe(world, LocationName.egg_quarters_5, player, + lambda state: state.has(ItemName.rouge_pick_nails, player) and + state.has(ItemName.rouge_treasure_scope, player)) + add_rule_safe(world, LocationName.lost_colony_5, player, + lambda state: state.has(ItemName.eggman_jet_engine, player) and + state.has(ItemName.eggman_large_cannon, player)) + add_rule_safe(world, LocationName.weapons_bed_5, player, + lambda state: state.has(ItemName.eggman_jet_engine, player)) + add_rule_safe(world, LocationName.security_hall_5, player, + lambda state: state.has(ItemName.rouge_treasure_scope, player)) + add_rule_safe(world, LocationName.mad_space_5, player, + lambda state: state.has(ItemName.rouge_iron_boots, player)) + add_rule_safe(world, LocationName.cosmic_wall_5, player, + lambda state: state.has(ItemName.eggman_jet_engine, player)) + + add_rule_safe(world, LocationName.cannon_core_5, player, + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.knuckles_mystic_melody, player) and + state.has(ItemName.knuckles_hammer_gloves, player)) + + # Upgrade Spot Upgrade Requirements add_rule(world.get_location(LocationName.city_escape_upgrade, player), lambda state: state.has(ItemName.sonic_bounce_bracelet, player) and state.has(ItemName.sonic_flame_ring, player)) @@ -357,30 +863,223 @@ def set_mission_upgrade_rules(world: MultiWorld, player: int): lambda state: state.has(ItemName.tails_bazooka, player)) add_rule(world.get_location(LocationName.hidden_base_upgrade, player), lambda state: state.has(ItemName.tails_booster, player) and - state.has(ItemName.tails_bazooka, player)) + (state.has(ItemName.tails_bazooka, player) or state.has(ItemName.tails_mystic_melody, player))) add_rule(world.get_location(LocationName.eternal_engine_upgrade, player), lambda state: state.has(ItemName.tails_booster, player)) add_rule(world.get_location(LocationName.meteor_herd_upgrade, player), lambda state: state.has(ItemName.knuckles_hammer_gloves, player)) - add_rule(world.get_location(LocationName.crazy_gadget_upgrade, player), - lambda state: state.has(ItemName.sonic_bounce_bracelet, player)) add_rule(world.get_location(LocationName.final_rush_upgrade, player), lambda state: state.has(ItemName.sonic_bounce_bracelet, player)) add_rule(world.get_location(LocationName.iron_gate_upgrade, player), - lambda state: state.has(ItemName.eggman_large_cannon, player)) + lambda state: state.has(ItemName.eggman_jet_engine, player) or + state.has(ItemName.eggman_large_cannon, player)) add_rule(world.get_location(LocationName.dry_lagoon_upgrade, player), lambda state: state.has(ItemName.rouge_pick_nails, player)) add_rule(world.get_location(LocationName.sand_ocean_upgrade, player), lambda state: state.has(ItemName.eggman_jet_engine, player)) - add_rule(world.get_location(LocationName.radical_highway_upgrade, player), - lambda state: state.has(ItemName.shadow_air_shoes, player)) add_rule(world.get_location(LocationName.security_hall_upgrade, player), - lambda state: state.has(ItemName.rouge_mystic_melody, player) and - state.has(ItemName.rouge_iron_boots, player)) + lambda state: state.has(ItemName.rouge_iron_boots, player)) add_rule(world.get_location(LocationName.cosmic_wall_upgrade, player), lambda state: state.has(ItemName.eggman_jet_engine, player)) + # Chao Key Upgrade Requirements + if world.keysanity[player]: + add_rule(world.get_location(LocationName.prison_lane_chao_1, player), + lambda state: state.has(ItemName.tails_bazooka, player)) + add_rule(world.get_location(LocationName.mission_street_chao_1, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.hidden_base_chao_1, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.death_chamber_chao_1, player), + lambda state: state.has(ItemName.knuckles_hammer_gloves, player)) + add_rule(world.get_location(LocationName.eternal_engine_chao_1, player), + lambda state: state.has(ItemName.tails_booster, player)) + + add_rule(world.get_location(LocationName.cosmic_wall_chao_1, player), + lambda state: state.has(ItemName.eggman_mystic_melody, player) and + state.has(ItemName.eggman_jet_engine, player)) + + add_rule(world.get_location(LocationName.cannon_core_chao_1, player), + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.eggman_jet_engine, player) and + state.has(ItemName.eggman_large_cannon, player)) + + add_rule(world.get_location(LocationName.prison_lane_chao_2, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.mission_street_chao_2, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.hidden_base_chao_2, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.death_chamber_chao_2, player), + lambda state: state.has(ItemName.knuckles_hammer_gloves, player)) + add_rule(world.get_location(LocationName.eternal_engine_chao_2, player), + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.tails_bazooka, player)) + add_rule(world.get_location(LocationName.crazy_gadget_chao_2, player), + lambda state: state.has(ItemName.sonic_bounce_bracelet, player)) + + add_rule(world.get_location(LocationName.weapons_bed_chao_2, player), + lambda state: state.has(ItemName.eggman_jet_engine, player)) + add_rule(world.get_location(LocationName.cosmic_wall_chao_2, player), + lambda state: state.has(ItemName.eggman_jet_engine, player)) + + add_rule(world.get_location(LocationName.cannon_core_chao_2, player), + lambda state: state.has(ItemName.tails_booster, player)) + + add_rule(world.get_location(LocationName.mission_street_chao_3, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.death_chamber_chao_3, player), + lambda state: state.has(ItemName.knuckles_shovel_claws, player) and + state.has(ItemName.knuckles_hammer_gloves, player)) + add_rule(world.get_location(LocationName.eternal_engine_chao_3, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.crazy_gadget_chao_3, player), + lambda state: state.has(ItemName.sonic_light_shoes, player) and + state.has(ItemName.sonic_flame_ring, player)) + add_rule(world.get_location(LocationName.final_rush_chao_3, player), + lambda state: state.has(ItemName.sonic_bounce_bracelet, player)) + + add_rule(world.get_location(LocationName.egg_quarters_chao_3, player), + lambda state: state.has(ItemName.rouge_mystic_melody, player)) + add_rule(world.get_location(LocationName.lost_colony_chao_3, player), + lambda state: state.has(ItemName.eggman_jet_engine, player)) + add_rule(world.get_location(LocationName.weapons_bed_chao_3, player), + lambda state: state.has(ItemName.eggman_jet_engine, player)) + add_rule(world.get_location(LocationName.security_hall_chao_3, player), + lambda state: state.has(ItemName.rouge_pick_nails, player)) + add_rule(world.get_location(LocationName.cosmic_wall_chao_3, player), + lambda state: state.has(ItemName.eggman_jet_engine, player)) + + add_rule(world.get_location(LocationName.cannon_core_chao_3, player), + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.knuckles_hammer_gloves, player) and + state.has(ItemName.sonic_flame_ring, player)) + + # Pipe Upgrade Requirements + if world.whistlesanity[player].value == 1 or world.whistlesanity[player].value == 3: + add_rule(world.get_location(LocationName.hidden_base_pipe_1, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.eternal_engine_pipe_1, player), + lambda state: state.has(ItemName.tails_booster, player)) + + add_rule(world.get_location(LocationName.cosmic_wall_pipe_1, player), + lambda state: state.has(ItemName.eggman_jet_engine, player)) + + add_rule(world.get_location(LocationName.mission_street_pipe_2, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.hidden_base_pipe_2, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.death_chamber_pipe_2, player), + lambda state: state.has(ItemName.knuckles_shovel_claws, player) and + state.has(ItemName.knuckles_hammer_gloves, player)) + add_rule(world.get_location(LocationName.eternal_engine_pipe_2, player), + lambda state: state.has(ItemName.tails_booster, player)) + + add_rule(world.get_location(LocationName.lost_colony_pipe_2, player), + lambda state: state.has(ItemName.eggman_jet_engine, player)) + add_rule(world.get_location(LocationName.cosmic_wall_pipe_2, player), + lambda state: state.has(ItemName.eggman_jet_engine, player)) + + add_rule(world.get_location(LocationName.cannon_core_pipe_2, player), + lambda state: state.has(ItemName.tails_booster, player)) + + add_rule(world.get_location(LocationName.prison_lane_pipe_3, player), + lambda state: state.has(ItemName.tails_bazooka, player)) + add_rule(world.get_location(LocationName.mission_street_pipe_3, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.hidden_base_pipe_3, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.death_chamber_pipe_3, player), + lambda state: state.has(ItemName.knuckles_shovel_claws, player) and + state.has(ItemName.knuckles_hammer_gloves, player)) + add_rule(world.get_location(LocationName.eternal_engine_pipe_3, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.crazy_gadget_pipe_3, player), + lambda state: state.has(ItemName.sonic_bounce_bracelet, player) or + state.has(ItemName.sonic_mystic_melody, player)) + + add_rule(world.get_location(LocationName.weapons_bed_pipe_3, player), + lambda state: state.has(ItemName.eggman_jet_engine, player)) + add_rule(world.get_location(LocationName.cosmic_wall_pipe_3, player), + lambda state: state.has(ItemName.eggman_jet_engine, player)) + + add_rule(world.get_location(LocationName.cannon_core_pipe_3, player), + lambda state: state.has(ItemName.tails_booster, player)) + + add_rule(world.get_location(LocationName.hidden_base_pipe_4, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.eternal_engine_pipe_4, player), + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.tails_bazooka, player)) + add_rule(world.get_location(LocationName.crazy_gadget_pipe_4, player), + lambda state: state.has(ItemName.sonic_light_shoes, player) and + state.has(ItemName.sonic_flame_ring, player)) + + add_rule(world.get_location(LocationName.weapons_bed_pipe_4, player), + lambda state: state.has(ItemName.eggman_jet_engine, player) and + state.has(ItemName.eggman_large_cannon, player)) + add_rule(world.get_location(LocationName.cosmic_wall_pipe_4, player), + lambda state: state.has(ItemName.eggman_jet_engine, player)) + + add_rule(world.get_location(LocationName.cannon_core_pipe_4, player), + lambda state: state.has(ItemName.tails_booster, player)) + + add_rule(world.get_location(LocationName.hidden_base_pipe_5, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.eternal_engine_pipe_5, player), + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.tails_bazooka, player)) + + add_rule(world.get_location(LocationName.weapons_bed_pipe_5, player), + lambda state: state.has(ItemName.eggman_jet_engine, player)) + add_rule(world.get_location(LocationName.cosmic_wall_pipe_5, player), + lambda state: state.has(ItemName.eggman_jet_engine, player)) + + add_rule(world.get_location(LocationName.cannon_core_pipe_5, player), + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.knuckles_hammer_gloves, player)) + + # Hidden Whistle Upgrade Requirements + if world.whistlesanity[player].value == 2 or world.whistlesanity[player].value == 3: + add_rule(world.get_location(LocationName.mission_street_hidden_3, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.death_chamber_hidden_1, player), + lambda state: state.has(ItemName.knuckles_shovel_claws, player) and + state.has(ItemName.knuckles_hammer_gloves, player)) + add_rule(world.get_location(LocationName.death_chamber_hidden_2, player), + lambda state: state.has(ItemName.knuckles_hammer_gloves, player)) + add_rule(world.get_location(LocationName.cannon_core_hidden_1, player), + lambda state: state.has(ItemName.tails_booster, player)) + + # Gold Beetle Upgrade Requirements + if world.beetlesanity[player]: + add_rule(world.get_location(LocationName.hidden_base_beetle, player), + lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.death_chamber_beetle, player), + lambda state: state.has(ItemName.knuckles_shovel_claws, player) and + state.has(ItemName.knuckles_hammer_gloves, player)) + add_rule(world.get_location(LocationName.eternal_engine_beetle, player), + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.tails_bazooka, player)) + add_rule(world.get_location(LocationName.crazy_gadget_beetle, player), + lambda state: state.has(ItemName.sonic_light_shoes, player) and + state.has(ItemName.sonic_flame_ring, player)) + + add_rule(world.get_location(LocationName.dry_lagoon_beetle, player), + lambda state: state.has(ItemName.rouge_mystic_melody, player) and + state.has(ItemName.rouge_pick_nails, player) and + state.has(ItemName.rouge_iron_boots, player)) + add_rule(world.get_location(LocationName.lost_colony_beetle, player), + lambda state: state.has(ItemName.eggman_jet_engine, player)) + add_rule(world.get_location(LocationName.cosmic_wall_beetle, player), + lambda state: state.has(ItemName.eggman_mystic_melody, player) and + state.has(ItemName.eggman_jet_engine, player)) + + add_rule(world.get_location(LocationName.cannon_core_beetle, player), + lambda state: state.has(ItemName.tails_booster, player) and + state.has(ItemName.knuckles_hammer_gloves, player)) + def set_boss_gate_rules(world: MultiWorld, player: int, gate_bosses: typing.Dict[int, int]): for x in range(len(gate_bosses)): @@ -389,12 +1088,15 @@ def set_boss_gate_rules(world: MultiWorld, player: int, gate_bosses: typing.Dict lambda state: state.has(ItemName.knuckles_shovel_claws, player)) -def set_rules(world: MultiWorld, player: int, gate_bosses: typing.Dict[int, int]): +def set_rules(world: MultiWorld, player: int, gate_bosses: typing.Dict[int, int], mission_map: typing.Dict[int, int], mission_count_map: typing.Dict[int, int]): # Mission Progression Rules (Mission 1 begets Mission 2, etc.) - set_mission_progress_rules(world, player) + set_mission_progress_rules(world, player, mission_map, mission_count_map) # Upgrade Requirements for each mission location - set_mission_upgrade_rules(world, player) + if world.logic_difficulty[player].value == 0: + set_mission_upgrade_rules_standard(world, player) + elif world.logic_difficulty[player].value == 1: + set_mission_upgrade_rules_hard(world, player) # Upgrade Requirements for each boss gate set_boss_gate_rules(world, player, gate_bosses) diff --git a/worlds/sa2b/__init__.py b/worlds/sa2b/__init__.py index f341df9e85ef..4e7443c24bfd 100644 --- a/worlds/sa2b/__init__.py +++ b/worlds/sa2b/__init__.py @@ -2,7 +2,7 @@ import math from BaseClasses import Item, MultiWorld, Tutorial, ItemClassification -from .Items import SA2BItem, ItemData, item_table, upgrades_table, junk_table, trap_table +from .Items import SA2BItem, ItemData, item_table, upgrades_table, emeralds_table, junk_table, trap_table, item_groups from .Locations import SA2BLocation, all_locations, setup_locations from .Options import sa2b_options from .Regions import create_regions, shuffleable_regions, connect_regions, LevelGate, gate_0_whitelist_regions, \ @@ -11,6 +11,7 @@ from .Names import ItemName, LocationName from ..AutoWorld import WebWorld, World from .GateBosses import get_gate_bosses, get_boss_name +from .Missions import get_mission_table, get_mission_count_table, get_first_and_last_cannons_core_missions import Patch @@ -29,7 +30,7 @@ class SA2BWeb(WebWorld): tutorials = [setup_en] -def check_for_impossible_shuffle(shuffled_levels: typing.List[int], gate_0_range: int, world: MultiWorld): +def check_for_impossible_shuffle(shuffled_levels: typing.List[int], gate_0_range: int, multiworld: MultiWorld): blacklist_level_count = 0 for i in range(gate_0_range): @@ -37,7 +38,7 @@ def check_for_impossible_shuffle(shuffled_levels: typing.List[int], gate_0_range blacklist_level_count += 1 if blacklist_level_count == gate_0_range: - index_to_swap = world.random.randint(0, gate_0_range) + index_to_swap = multiworld.random.randint(0, gate_0_range) for i in range(len(shuffled_levels)): if shuffled_levels[i] in gate_0_whitelist_regions: shuffled_levels[i], shuffled_levels[index_to_swap] = shuffled_levels[index_to_swap], shuffled_levels[i] @@ -51,14 +52,17 @@ class SA2BWorld(World): game: str = "Sonic Adventure 2 Battle" option_definitions = sa2b_options topology_present = False - data_version = 2 + data_version = 3 + item_name_groups = item_groups item_name_to_id = {name: data.code for name, data in item_table.items()} location_name_to_id = all_locations location_table: typing.Dict[str, int] music_map: typing.Dict[int, int] + mission_map: typing.Dict[int, int] + mission_count_map: typing.Dict[int, int] emblems_for_cannons_core: int region_emblem_map: typing.Dict[int, int] gate_costs: typing.Dict[int, int] @@ -67,15 +71,22 @@ class SA2BWorld(World): def _get_slot_data(self): return { - "ModVersion": 101, + "ModVersion": 200, + "Goal": self.multiworld.goal[self.player].value, "MusicMap": self.music_map, + "MissionMap": self.mission_map, + "MissionCountMap": self.mission_count_map, "MusicShuffle": self.multiworld.music_shuffle[self.player].value, + "Narrator": self.multiworld.narrator[self.player].value, "RequiredRank": self.multiworld.required_rank[self.player].value, + "ChaoKeys": self.multiworld.keysanity[self.player].value, + "Whistlesanity": self.multiworld.whistlesanity[self.player].value, + "GoldBeetles": self.multiworld.beetlesanity[self.player].value, "ChaoRaceChecks": self.multiworld.chao_race_checks[self.player].value, "ChaoGardenDifficulty": self.multiworld.chao_garden_difficulty[self.player].value, "DeathLink": self.multiworld.death_link[self.player].value, - "IncludeMissions": self.multiworld.include_missions[self.player].value, "EmblemPercentageForCannonsCore": self.multiworld.emblem_percentage_for_cannons_core[self.player].value, + "RequiredCannonsCoreMissions": self.multiworld.required_cannons_core_missions[self.player].value, "NumberOfLevelGates": self.multiworld.number_of_level_gates[self.player].value, "LevelGateDistribution": self.multiworld.level_gate_distribution[self.player].value, "EmblemsForCannonsCore": self.emblems_for_cannons_core, @@ -137,7 +148,10 @@ def generate_early(self): self.gate_bosses = get_gate_bosses(self.multiworld, self.player) def generate_basic(self): - self.multiworld.get_location(LocationName.biolizard, self.player).place_locked_item(self.create_item(ItemName.maria)) + if self.multiworld.goal[self.player].value == 0 or self.multiworld.goal[self.player].value == 2: + self.multiworld.get_location(LocationName.finalhazard, self.player).place_locked_item(self.create_item(ItemName.maria)) + elif self.multiworld.goal[self.player].value == 1: + self.multiworld.get_location(LocationName.green_hill, self.player).place_locked_item(self.create_item(ItemName.maria)) itempool: typing.List[SA2BItem] = [] @@ -149,6 +163,11 @@ def generate_basic(self): for item in {**upgrades_table}: itempool += self._create_items(item) + if self.multiworld.goal[self.player].value == 1 or self.multiworld.goal[self.player].value == 2: + # Some flavor of Chaos Emerald Hunt + for item in {**emeralds_table}: + itempool += self._create_items(item) + # Cap at 180 Emblems raw_emblem_count = total_required_locations - len(itempool) total_emblem_count = min(raw_emblem_count, 180) @@ -195,7 +214,9 @@ def generate_basic(self): self.region_emblem_map = dict(zip(shuffled_region_list, emblem_requirement_list)) - connect_regions(self.multiworld, self.player, gates, self.emblems_for_cannons_core, self.gate_bosses) + first_cannons_core_mission, final_cannons_core_mission = get_first_and_last_cannons_core_missions(self.mission_map, self.mission_count_map) + + connect_regions(self.multiworld, self.player, gates, self.emblems_for_cannons_core, self.gate_bosses, first_cannons_core_mission, final_cannons_core_mission) max_required_emblems = max(max(emblem_requirement_list), self.emblems_for_cannons_core) itempool += [self.create_item(ItemName.emblem) for _ in range(max_required_emblems)] @@ -210,6 +231,9 @@ def generate_basic(self): trap_weights += ([ItemName.timestop_trap] * self.multiworld.timestop_trap_weight[self.player].value) trap_weights += ([ItemName.confuse_trap] * self.multiworld.confusion_trap_weight[self.player].value) trap_weights += ([ItemName.tiny_trap] * self.multiworld.tiny_trap_weight[self.player].value) + trap_weights += ([ItemName.gravity_trap] * self.multiworld.gravity_trap_weight[self.player].value) + trap_weights += ([ItemName.exposition_trap] * self.multiworld.exposition_trap_weight[self.player].value) + #trap_weights += ([ItemName.darkness_trap] * self.multiworld.darkness_trap_weight[self.player].value) junk_count += extra_junk_count trap_count = 0 if (len(trap_weights) == 0) else math.ceil(junk_count * (self.multiworld.trap_fill_percentage[self.player].value / 100.0)) @@ -237,17 +261,59 @@ def generate_basic(self): musiclist_o = list(range(0, 47)) musiclist_s = musiclist_o.copy() self.multiworld.random.shuffle(musiclist_s) + musiclist_o.extend(range(47, 78)) + musiclist_s.extend(range(47, 78)) + + if self.multiworld.sadx_music[self.player].value == 1: + musiclist_s = [x+100 for x in musiclist_s] + elif self.multiworld.sadx_music[self.player].value == 2: + for i in range(len(musiclist_s)): + if self.multiworld.random.randint(0,1): + musiclist_s[i] += 100 + self.music_map = dict(zip(musiclist_o, musiclist_s)) elif self.multiworld.music_shuffle[self.player] == "full": musiclist_o = list(range(0, 78)) musiclist_s = musiclist_o.copy() self.multiworld.random.shuffle(musiclist_s) + + if self.multiworld.sadx_music[self.player].value == 1: + musiclist_s = [x+100 for x in musiclist_s] + elif self.multiworld.sadx_music[self.player].value == 2: + for i in range(len(musiclist_s)): + if self.multiworld.random.randint(0,1): + musiclist_s[i] += 100 + + self.music_map = dict(zip(musiclist_o, musiclist_s)) + elif self.multiworld.music_shuffle[self.player] == "singularity": + musiclist_o = list(range(0, 78)) + musiclist_s = [self.multiworld.random.choice(musiclist_o)] * len(musiclist_o) + + if self.multiworld.sadx_music[self.player].value == 1: + musiclist_s = [x+100 for x in musiclist_s] + elif self.multiworld.sadx_music[self.player].value == 2: + if self.multiworld.random.randint(0,1): + musiclist_s = [x+100 for x in musiclist_s] + self.music_map = dict(zip(musiclist_o, musiclist_s)) else: - self.music_map = dict() + musiclist_o = list(range(0, 78)) + musiclist_s = musiclist_o.copy() + + if self.multiworld.sadx_music[self.player].value == 1: + musiclist_s = [x+100 for x in musiclist_s] + elif self.multiworld.sadx_music[self.player].value == 2: + for i in range(len(musiclist_s)): + if self.multiworld.random.randint(0,1): + musiclist_s[i] += 100 + + self.music_map = dict(zip(musiclist_o, musiclist_s)) def create_regions(self): - self.location_table = setup_locations(self.multiworld, self.player) + self.mission_map = get_mission_table(self.multiworld, self.player) + self.mission_count_map = get_mission_count_table(self.multiworld, self.player) + + self.location_table = setup_locations(self.multiworld, self.player, self.mission_map, self.mission_count_map) create_regions(self.multiworld, self.player, self.location_table) def create_item(self, name: str, force_non_progression=False) -> Item: @@ -268,18 +334,52 @@ def create_item(self, name: str, force_non_progression=False) -> Item: return created_item + def get_filler_item_name(self) -> str: + self.multiworld.random.choice(junk_table.keys()) + def set_rules(self): - set_rules(self.multiworld, self.player, self.gate_bosses) + set_rules(self.multiworld, self.player, self.gate_bosses, self.mission_map, self.mission_count_map) def write_spoiler(self, spoiler_handle: typing.TextIO): - spoiler_handle.write("\n") - header_text = "Sonic Adventure 2 Bosses for {}:\n" - header_text = header_text.format(self.multiworld.player_name[self.player]) - spoiler_handle.write(header_text) - for x in range(len(self.gate_bosses.values())): - text = "Gate {0} Boss: {1}\n" - text = text.format((x + 1), get_boss_name(self.gate_bosses[x + 1])) - spoiler_handle.writelines(text) + if self.multiworld.number_of_level_gates[self.player].value > 0: + spoiler_handle.write("\n") + header_text = "Sonic Adventure 2 Bosses for {}:\n" + header_text = header_text.format(self.multiworld.player_name[self.player]) + spoiler_handle.write(header_text) + for x in range(len(self.gate_bosses.values())): + text = "Gate {0} Boss: {1}\n" + text = text.format((x + 1), get_boss_name(self.gate_bosses[x + 1])) + spoiler_handle.writelines(text) + + def extend_hint_information(self, hint_data: typing.Dict[int, typing.Dict[int, str]]): + gate_names = [ + LocationName.gate_0_region, + LocationName.gate_1_region, + LocationName.gate_2_region, + LocationName.gate_3_region, + LocationName.gate_4_region, + LocationName.gate_5_region, + ] + no_hint_region_names = [ + LocationName.cannon_core_region, + LocationName.chao_garden_beginner_region, + LocationName.chao_garden_intermediate_region, + LocationName.chao_garden_expert_region, + ] + er_hint_data = {} + for i in range(self.multiworld.number_of_level_gates[self.player].value + 1): + gate_name = gate_names[i] + gate_region = self.multiworld.get_region(gate_name, self.player) + if not gate_region: + continue + for exit in gate_region.exits: + if exit.connected_region.name in gate_names or exit.connected_region.name in no_hint_region_names: + continue + level_region = exit.connected_region + for location in level_region.locations: + er_hint_data[location.address] = gate_name + + hint_data[self.player] = er_hint_data @classmethod def stage_fill_hook(cls, world, progitempool, usefulitempool, filleritempool, fill_locations): diff --git a/worlds/sa2b/docs/en_Sonic Adventure 2 Battle.md b/worlds/sa2b/docs/en_Sonic Adventure 2 Battle.md index b70037a93857..89ff80e9c456 100644 --- a/worlds/sa2b/docs/en_Sonic Adventure 2 Battle.md +++ b/worlds/sa2b/docs/en_Sonic Adventure 2 Battle.md @@ -6,15 +6,15 @@ The [player settings page for this game](../player-settings) contains all the op ## What does randomization do to this game? -The randomizer shuffles emblems and upgrade items into the AP item pool. The story mode is disabled, but stage select is available from the start. Levels can be locked behind gates requiring a certain number of emblems and a boss fight to unlock. Cannons Core will be locked behind a percentage of all available emblems, and completing Cannons Core will unlock the Biolizard boss. Progress towards unlocking Cannons Core and the next stage gate will be displayed on the Stage Select screen. +The randomizer shuffles emblems and upgrade items into the AP item pool. The story mode is disabled, but stage select is available from the start. Levels can be locked behind gates requiring a certain number of emblems and a boss fight to unlock. Cannon's Core will be locked behind a percentage of all available emblems, and completing Cannon's Core will unlock the Biolizard boss if Biolizard is the goal. If the emerald hunt goal is selected, collecting all seven Chaos Emeralds will unlock Green Hill Zone. Progress towards unlocking Cannon's Core and the next stage gate will be displayed on the Stage Select screen. ## What is the goal of Sonic Adventure 2: Battle when randomized? -The goal is to unlock and complete Cannons Core Mission 1, then complete the Biolizard boss fight. +If the Biolizard goal is selected, the objective is to unlock and complete the required number of Cannon's Core Missions, then complete the Biolizard boss fight. If the Emerald Hunt goal is selected, the objective is to find the seven Chaos Emeralds then complete Green Hill Zone and optionally default Final Hazard. ## What items and locations get shuffled? -All 30 story stages leading up to Cannons Core will be shuffled and can be optionally placed behind emblem requirement gates. Chao Garden emblems can optionally be added to the randomizer. All emblems from the selected mission range and all 28 character upgrade items get shuffled. At most 180 emblems will be added to the item pool, after which remaining items added will be random collectables (rings, shields, etc). Traps can also be optionally included. +All 30 story stages leading up to Cannon's Core will be shuffled and can be optionally placed behind emblem requirement gates. Mission order can be shuffled for each stage. Chao keys, animal pipes, hidden whistle spots, and gold beetles can be added as additional locations to check in each stage. Chao Garden emblems can optionally be added to the randomizer. All emblems from the selected mission range and all 28 character upgrade items get shuffled. At most 180 emblems will be added to the item pool, after which remaining items added will be random collectables (rings, shields, etc). Traps can also be optionally included. ## Which items can be in another player's world? @@ -22,11 +22,11 @@ Any shuffled item can be in other players' worlds. ## What does another world's item look like in Sonic Adventure 2: Battle -Emblems have no visualization in the randomizer and items all retain their original appearance. You won't know if an item belongs to another player until you collect. +Emblems have no visualization in the randomizer and items all retain their original appearance. Chao Keys will appear as an Archipelago logo even if the keysanity option is disabled. Despite the appearance, they will only count as a check if the keysanity option is enabled. You won't know if an item belongs to another player until you collect. ## When the player receives an emblem or item, what happens? -When the player collects an emblem or item, text will appear on screen to indicate who the item was for and what the item was. When collecting items in a level, the orignal item collection text will display and will likely not be correct. +When the player collects an emblem or item, text will appear on screen to indicate who the item was for and what the item was. When collecting an upgrade location, the orignal item collection text will display and will likely not be correct. ## How can I get started? diff --git a/worlds/sa2b/docs/setup_en.md b/worlds/sa2b/docs/setup_en.md index 1fa45dd799c1..41a48e66aefe 100644 --- a/worlds/sa2b/docs/setup_en.md +++ b/worlds/sa2b/docs/setup_en.md @@ -54,6 +54,14 @@ Some additional settings related to the Archipelago messages in game can be adju - Message Display Duration: This dictates how long Archipelago messages are displayed on screen (in seconds). - Message Font Size: The is the size of the font used to display the messages from Archipelago. +If you wish to use the `SADX Music` option of the Randomizer, you must own a copy of `Sonic Adventure DX` on Steam, and follow these steps: + +1. Find the folder on your PC where `Sonic Adventure DX` is installed. + +2. Enter the `SoundData` folder in the `Sonic Adventure DX` installation folder, and copy the `bgm` folder. + +3. Paste the `bgm` folder into the `ADX` folder which exists within the `gd_PC` folder in your `SA2B_Archipelago` mod folder. + ## Troubleshooting - "The following mods didn't load correctly: SA2B_Archipelago: DLL error - The specified module could not be found." @@ -92,6 +100,12 @@ Some additional settings related to the Archipelago messages in game can be adju - No resolution options in the Launcher.exe. - In the `Graphics device` dropdown, select the device and display you plan to run the game on. The `Resolution` dropdown should populate once a graphics device is selected. + +- No music is playing in the game. + - If you enabled an `SADX Music` option, then most likely the music data was not copied properly into the mod folder (See Additional Options for instructions). + +- Mission 1 is missing a texture in the stage select UI. + - Most likely another mod is conflicting and overwriting the texture pack. It is recommeded to have the SA2B Archipelago mod load last in the mod loader. ## Save File Safeguard (Advanced Option) From 7c3af68e59f45eb99c3c4706e9a7160928d78443 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Wed, 7 Dec 2022 06:37:47 +0100 Subject: [PATCH 21/35] ItemLinks: allow linking replacement items as well (#1274) --- BaseClasses.py | 24 ++++++++++++++------- Main.py | 10 ++++++--- Options.py | 8 +++++-- worlds/generic/docs/advanced_settings_en.md | 3 ++- 4 files changed, 31 insertions(+), 14 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index f037c84451c7..8327dbeb9ce6 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -26,6 +26,7 @@ class Group(TypedDict, total=False): replacement_items: Dict[int, Optional[str]] local_items: Set[str] non_local_items: Set[str] + link_replacement: bool class MultiWorld(): @@ -222,27 +223,32 @@ def set_options(self, args: Namespace) -> None: def set_item_links(self): item_links = {} - + replacement_prio = [False, True, None] for player in self.player_ids: for item_link in self.item_links[player].value: if item_link["name"] in item_links: if item_links[item_link["name"]]["game"] != self.game[player]: raise Exception(f"Cannot ItemLink across games. Link: {item_link['name']}") - item_links[item_link["name"]]["players"][player] = item_link["replacement_item"] - item_links[item_link["name"]]["item_pool"] &= set(item_link["item_pool"]) - item_links[item_link["name"]]["exclude"] |= set(item_link.get("exclude", [])) - item_links[item_link["name"]]["local_items"] &= set(item_link.get("local_items", [])) - item_links[item_link["name"]]["non_local_items"] &= set(item_link.get("non_local_items", [])) + current_link = item_links[item_link["name"]] + current_link["players"][player] = item_link["replacement_item"] + current_link["item_pool"] &= set(item_link["item_pool"]) + current_link["exclude"] |= set(item_link.get("exclude", [])) + current_link["local_items"] &= set(item_link.get("local_items", [])) + current_link["non_local_items"] &= set(item_link.get("non_local_items", [])) + current_link["link_replacement"] = min(current_link["link_replacement"], + replacement_prio.index(item_link["link_replacement"])) else: if item_link["name"] in self.player_name.values(): - raise Exception(f"Cannot name a ItemLink group the same as a player ({item_link['name']}) ({self.get_player_name(player)}).") + raise Exception(f"Cannot name a ItemLink group the same as a player ({item_link['name']}) " + f"({self.get_player_name(player)}).") item_links[item_link["name"]] = { "players": {player: item_link["replacement_item"]}, "item_pool": set(item_link["item_pool"]), "exclude": set(item_link.get("exclude", [])), "game": self.game[player], "local_items": set(item_link.get("local_items", [])), - "non_local_items": set(item_link.get("non_local_items", [])) + "non_local_items": set(item_link.get("non_local_items", [])), + "link_replacement": replacement_prio.index(item_link["link_replacement"]), } for name, item_link in item_links.items(): @@ -267,10 +273,12 @@ def set_item_links(self): for group_name, item_link in item_links.items(): game = item_link["game"] group_id, group = self.add_group(group_name, game, set(item_link["players"])) + group["item_pool"] = item_link["item_pool"] group["replacement_items"] = item_link["players"] group["local_items"] = item_link["local_items"] group["non_local_items"] = item_link["non_local_items"] + group["link_replacement"] = replacement_prio[item_link["link_replacement"]] # intended for unittests def set_default_common_options(self): diff --git a/Main.py b/Main.py index 1e7de31cd791..31351fc5f5e4 100644 --- a/Main.py +++ b/Main.py @@ -210,11 +210,15 @@ def find_common_pool(players: Set[int], shared_pool: Set[str]) -> Tuple[ while itemcount > len(world.itempool): items_to_add = [] for player in group["players"]: + if group["link_replacement"]: + item_player = group_id + else: + item_player = player if group["replacement_items"][player]: - items_to_add.append( - AutoWorld.call_single(world, "create_item", player, group["replacement_items"][player])) + items_to_add.append(AutoWorld.call_single(world, "create_item", item_player, + group["replacement_items"][player])) else: - items_to_add.append(AutoWorld.call_single(world, "create_filler", player)) + items_to_add.append(AutoWorld.call_single(world, "create_filler", item_player)) world.random.shuffle(items_to_add) world.itempool.extend(items_to_add[:itemcount - len(world.itempool)]) diff --git a/Options.py b/Options.py index 16deb90d77c9..b386eb8e4609 100644 --- a/Options.py +++ b/Options.py @@ -927,7 +927,8 @@ class ItemLinks(OptionList): Optional("exclude"): [And(str, len)], "replacement_item": Or(And(str, len), None), Optional("local_items"): [And(str, len)], - Optional("non_local_items"): [And(str, len)] + Optional("non_local_items"): [And(str, len)], + Optional("link_replacement"): Or(None, bool), } ]) @@ -950,6 +951,7 @@ def verify_items(items: typing.List[str], item_link: str, pool_name: str, world, return pool def verify(self, world, player_name: str, plando_options) -> None: + link: dict super(ItemLinks, self).verify(world, player_name, plando_options) existing_links = set() for link in self.value: @@ -974,7 +976,9 @@ def verify(self, world, player_name: str, plando_options) -> None: intersection = local_items.intersection(non_local_items) if intersection: - raise Exception(f"item_link {link['name']} has {intersection} items in both its local_items and non_local_items pool.") + raise Exception(f"item_link {link['name']} has {intersection} " + f"items in both its local_items and non_local_items pool.") + link.setdefault("link_replacement", None) per_game_common_options = { diff --git a/worlds/generic/docs/advanced_settings_en.md b/worlds/generic/docs/advanced_settings_en.md index a96598a8da46..b96b1d57e42d 100644 --- a/worlds/generic/docs/advanced_settings_en.md +++ b/worlds/generic/docs/advanced_settings_en.md @@ -184,6 +184,7 @@ A Link to the Past: - Fire Rod - Ice Rod replacement_item: "Rupee (1)" + link_replacement: true triggers: - option_category: A Link to the Past option_name: smallkey_shuffle @@ -241,7 +242,7 @@ Timespinner: * `exclude_locations` forces a not important item to be placed on the `Cave 45` location. * `item_links` * For `A Link to the Past` all players in the `rods` item link group will share their fire and ice rods and the player - items will be replaced with single rupees. + items will be replaced with single rupees. The rupee will also be shared among those players. * For `Timespinner` all players in the `TSAll` item link group will share their entire item pool and the `Twin Pyramid Key` and `Timespinner Wheel` will be forced among the worlds of those in the group. The `null` replacement item will, instead of forcing a specific chosen item, allow the generator to randomly pick a filler item to replace the player items. From 5273812039d521b2be1f7a43f724d0d73766d2b9 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Wed, 7 Dec 2022 06:40:30 +0100 Subject: [PATCH 22/35] Core: default distribute Factorio and Subnautica as .apworld (#1260) --- .gitignore | 2 +- inno_setup.iss | 1 + setup.py | 25 +++++++++++++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 0caf00a97824..4cb23a47aec4 100644 --- a/.gitignore +++ b/.gitignore @@ -48,7 +48,7 @@ Output Logs/ /freeze_requirements.txt /Archipelago.zip /setup.ini - +/installdelete.iss # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/inno_setup.iss b/inno_setup.iss index 1a4a6a56ba07..d8901c562a85 100644 --- a/inno_setup.iss +++ b/inno_setup.iss @@ -152,6 +152,7 @@ Type: dirifempty; Name: "{app}" [InstallDelete] Type: files; Name: "{app}\ArchipelagoLttPClient.exe" Type: filesandordirs; Name: "{app}\lib\worlds\rogue-legacy*" +#include "installdelete.iss" [Registry] diff --git a/setup.py b/setup.py index 63af13417303..fa739c00ade9 100644 --- a/setup.py +++ b/setup.py @@ -3,6 +3,7 @@ import sys import sysconfig import platform +import zipfile from pathlib import Path from hashlib import sha3_512 import base64 @@ -14,6 +15,11 @@ from Launcher import components, icon_paths +apworlds: set = { + "Subnautica", + "Factorio", +} + # This is a bit jank. We need cx-Freeze to be able to run anything from this script, so install it import subprocess import pkg_resources @@ -185,11 +191,26 @@ def run(self): from WebHostLib.options import create create() from worlds.AutoWorld import AutoWorldRegister + assert not apworlds - set(AutoWorldRegister.world_types), "Unknown world designated for .apworld" + folders_to_remove: typing.List[str] = [] for worldname, worldtype in AutoWorldRegister.world_types.items(): if not worldtype.hidden: file_name = worldname+".yaml" shutil.copyfile(os.path.join("WebHostLib", "static", "generated", "configs", file_name), self.buildfolder / "Players" / "Templates" / file_name) + if worldname in apworlds: + file_name = os.path.split(os.path.dirname(worldtype.__file__))[1] + world_directory = self.libfolder / "worlds" / file_name + # this method creates an apworld that cannot be moved to a different OS or minor python version, + # which should be ok + with zipfile.ZipFile(self.libfolder / "worlds" / (file_name + ".apworld"), "x", zipfile.ZIP_DEFLATED, + compresslevel=9) as zf: + entry: os.DirEntry + for path in world_directory.rglob("*.*"): + relative_path = os.path.join(*path.parts[path.parts.index("worlds")+1:]) + zf.write(path, relative_path) + folders_to_remove.append(file_name) + shutil.rmtree(world_directory) shutil.copyfile("meta.yaml", self.buildfolder / "Players" / "Templates" / "meta.yaml") # TODO: fix LttP options one day shutil.copyfile("playerSettings.yaml", self.buildfolder / "Players" / "Templates" / "A Link to the Past.yaml") @@ -218,9 +239,13 @@ def run(self): self.create_manifest() if is_windows: + # Inno setup stuff with open("setup.ini", "w") as f: min_supported_windows = "6.2.9200" if sys.version_info > (3, 9) else "6.0.6000" f.write(f"[Data]\nsource_path={self.buildfolder}\nmin_windows={min_supported_windows}\n") + with open("installdelete.iss", "w") as f: + f.writelines("Type: filesandordirs; Name: \"{app}\\lib\\worlds\\"+world_directory+"\"\n" + for world_directory in folders_to_remove) else: # make sure extra programs are executable enemizer_exe = self.buildfolder / 'EnemizerCLI/EnemizerCLI.Core' From e206c065bf8e0892dd2d00c511d3ae225bb41318 Mon Sep 17 00:00:00 2001 From: Alchav <59858495+Alchav@users.noreply.github.com> Date: Wed, 7 Dec 2022 18:38:34 -0500 Subject: [PATCH 23/35] =?UTF-8?q?Pok=C3=A9mon=20Red=20and=20Blue:=20Versio?= =?UTF-8?q?n=202=20(#1282)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds Trainersanity option (Each non-scripted trainer has a location check, adding 317 locations) Adds Randomize Pokedex option. It is required to obtain items from Oak's Aides. Adds option to add all normal shop items to all normal shops. Adds DeathLink option. Adds traps. Improves Type Chart randomization. Items can be received during battle. Stores start inventory in ROM. Only requests remote start inventory if patch is from v1. Fixes logic bugs. Various other improvements. --- PokemonClient.py | 46 +- Utils.py | 2 +- data/lua/PKMN_RB/pkmn_rb.lua | 39 +- worlds/pokemon_rb/__init__.py | 64 +- worlds/pokemon_rb/basepatch_blue.bsdiff4 | Bin 29579 -> 36544 bytes worlds/pokemon_rb/basepatch_red.bsdiff4 | Bin 29743 -> 36528 bytes .../docs/en_Pokemon Red and Blue.md | 1 + worlds/pokemon_rb/docs/setup_en.md | 4 +- worlds/pokemon_rb/items.py | 12 +- worlds/pokemon_rb/locations.py | 486 ++++++-- worlds/pokemon_rb/logic.py | 9 + worlds/pokemon_rb/options.py | 170 ++- worlds/pokemon_rb/regions.py | 516 ++++---- worlds/pokemon_rb/rom.py | 173 ++- worlds/pokemon_rb/rom_addresses.py | 1088 +++++++++++------ worlds/pokemon_rb/rules.py | 35 +- 16 files changed, 1775 insertions(+), 870 deletions(-) diff --git a/PokemonClient.py b/PokemonClient.py index f71efbcfad49..d5f6e09fc6a8 100644 --- a/PokemonClient.py +++ b/PokemonClient.py @@ -39,6 +39,8 @@ DISPLAY_MSGS = True +SCRIPT_VERSION = 1 + class GBCommandProcessor(ClientCommandProcessor): def __init__(self, ctx: CommonContext): @@ -53,7 +55,6 @@ def _cmd_gb(self): class GBContext(CommonContext): command_processor = GBCommandProcessor game = 'Pokemon Red and Blue' - items_handling = 0b101 def __init__(self, server_address, password): super().__init__(server_address, password) @@ -64,6 +65,10 @@ def __init__(self, server_address, password): self.gb_status = CONNECTION_INITIAL_STATUS self.awaiting_rom = False self.display_msgs = True + self.deathlink_pending = False + self.set_deathlink = False + self.client_compatibility_mode = 0 + self.items_handling = 0b001 async def server_auth(self, password_requested: bool = False): if password_requested and not self.password: @@ -82,6 +87,8 @@ def _set_message(self, msg: str, msg_id: int): def on_package(self, cmd: str, args: dict): if cmd == 'Connected': self.locations_array = None + if 'death_link' in args['slot_data'] and args['slot_data']['death_link']: + self.set_deathlink = True elif cmd == "RoomInfo": self.seed_name = args['seed_name'] elif cmd == 'Print': @@ -92,6 +99,10 @@ def on_package(self, cmd: str, args: dict): msg = f"Received {', '.join([self.item_names[item.item] for item in args['items']])}" self._set_message(msg, SYSTEM_MESSAGE_ID) + def on_deathlink(self, data: dict): + self.deathlink_pending = True + super().on_deathlink(data) + def run_gui(self): from kvui import GameManager @@ -107,13 +118,16 @@ class GBManager(GameManager): def get_payload(ctx: GBContext): current_time = time.time() - return json.dumps( + ret = json.dumps( { "items": [item.item for item in ctx.items_received], "messages": {f'{key[0]}:{key[1]}': value for key, value in ctx.messages.items() - if key[0] > current_time - 10} + if key[0] > current_time - 10}, + "deathlink": ctx.deathlink_pending } ) + ctx.deathlink_pending = False + return ret async def parse_locations(data: List, ctx: GBContext): @@ -121,14 +135,8 @@ async def parse_locations(data: List, ctx: GBContext): flags = {"EventFlag": data[:0x140], "Missable": data[0x140:0x140 + 0x20], "Hidden": data[0x140 + 0x20: 0x140 + 0x20 + 0x0E], "Rod": data[0x140 + 0x20 + 0x0E:]} - # Check for clear problems if len(flags['Rod']) > 1: return - if flags["EventFlag"][1] + flags["EventFlag"][8] + flags["EventFlag"][9] + flags["EventFlag"][12] \ - + flags["EventFlag"][61] + flags["EventFlag"][62] + flags["EventFlag"][63] + flags["EventFlag"][64] \ - + flags["EventFlag"][65] + flags["EventFlag"][66] + flags["EventFlag"][67] + flags["EventFlag"][68] \ - + flags["EventFlag"][69] + flags["EventFlag"][70] != 0: - return for flag_type, loc_map in location_map.items(): for flag, loc_id in loc_map.items(): @@ -168,8 +176,15 @@ async def gb_sync_task(ctx: GBContext): # 2. An array representing the memory values of the locations area (if in game) data = await asyncio.wait_for(reader.readline(), timeout=5) data_decoded = json.loads(data.decode()) - #print(data_decoded) - + if 'scriptVersion' not in data_decoded or data_decoded['scriptVersion'] != SCRIPT_VERSION: + msg = "You are connecting with an incompatible Lua script version. Ensure your connector Lua " \ + "and PokemonClient are from the same Archipelago installation." + logger.info(msg, extra={'compact_gui': True}) + ctx.gui_error('Error', msg) + error_status = CONNECTION_RESET_STATUS + ctx.client_compatibility_mode = data_decoded['clientCompatibilityVersion'] + if ctx.client_compatibility_mode == 0: + ctx.items_handling = 0b101 # old patches will not have local start inventory, must be requested if ctx.seed_name and ctx.seed_name != ''.join([chr(i) for i in data_decoded['seedName'] if i != 0]): msg = "The server is running a different multiworld than your client is. (invalid seed_name)" logger.info(msg, extra={'compact_gui': True}) @@ -179,13 +194,20 @@ async def gb_sync_task(ctx: GBContext): if not ctx.auth: ctx.auth = ''.join([chr(i) for i in data_decoded['playerName'] if i != 0]) if ctx.auth == '': - logger.info("Invalid ROM detected. No player name built into the ROM.") + msg = "Invalid ROM detected. No player name built into the ROM." + logger.info(msg, extra={'compact_gui': True}) + ctx.gui_error('Error', msg) + error_status = CONNECTION_RESET_STATUS if ctx.awaiting_rom: await ctx.server_auth(False) if 'locations' in data_decoded and ctx.game and ctx.gb_status == CONNECTION_CONNECTED_STATUS \ and not error_status and ctx.auth: # Not just a keep alive ping, parse async_start(parse_locations(data_decoded['locations'], ctx)) + if 'deathLink' in data_decoded and data_decoded['deathLink'] and 'DeathLink' in ctx.tags: + await ctx.send_death(ctx.auth + " is out of usable Pokémon! " + ctx.auth + " blacked out!") + if ctx.set_deathlink: + await ctx.update_death_link(True) except asyncio.TimeoutError: logger.debug("Read Timed Out, Reconnecting") error_status = CONNECTION_TIMING_OUT_STATUS diff --git a/Utils.py b/Utils.py index 592207807c69..fdb86e63a8f5 100644 --- a/Utils.py +++ b/Utils.py @@ -38,7 +38,7 @@ class Version(typing.NamedTuple): build: int -__version__ = "0.3.6" +__version__ = "0.3.7" version_tuple = tuplize_version(__version__) is_linux = sys.platform.startswith("linux") diff --git a/data/lua/PKMN_RB/pkmn_rb.lua b/data/lua/PKMN_RB/pkmn_rb.lua index c439d53b0792..eaf751654762 100644 --- a/data/lua/PKMN_RB/pkmn_rb.lua +++ b/data/lua/PKMN_RB/pkmn_rb.lua @@ -7,18 +7,25 @@ local STATE_TENTATIVELY_CONNECTED = "Tentatively Connected" local STATE_INITIAL_CONNECTION_MADE = "Initial Connection Made" local STATE_UNINITIALIZED = "Uninitialized" +local SCRIPT_VERSION = 1 + local APIndex = 0x1A6E +local APDeathLinkAddress = 0x00FD local APItemAddress = 0x00FF local EventFlagAddress = 0x1735 local MissableAddress = 0x161A local HiddenItemsAddress = 0x16DE local RodAddress = 0x1716 local InGame = 0x1A71 +local ClientCompatibilityAddress = 0xFF00 local ItemsReceived = nil local playerName = nil local seedName = nil +local deathlink_rec = nil +local deathlink_send = false + local prevstate = "" local curstate = STATE_UNINITIALIZED local gbSocket = nil @@ -69,11 +76,10 @@ function processBlock(block) end local itemsBlock = block["items"] memDomain.wram() - if itemsBlock ~= nil then-- and u8(0x116B) ~= 0x00 then - -- print(itemsBlock) - ItemsReceived = itemsBlock - + if itemsBlock ~= nil then + ItemsReceived = itemsBlock end + deathlink_rec = block["deathlink"] end function difference(a, b) @@ -104,14 +110,7 @@ function generateLocationsChecked() return data end -function generateSerialData() - memDomain.wram() - status = u8(0x1A73) - if status == 0 then - return nil - end - return uRange(0x1A76, u8(0x1A74)) -end + local function arrayEqual(a1, a2) if #a1 ~= #a2 then return false @@ -135,7 +134,6 @@ function receive() curstate = STATE_UNINITIALIZED return elseif e == 'timeout' then - print("timeout") return elseif e ~= nil then print(e) @@ -157,16 +155,16 @@ function receive() playerName = newPlayerName seedName = newSeedName local retTable = {} + retTable["scriptVersion"] = SCRIPT_VERSION + retTable["clientCompatibilityVersion"] = u8(ClientCompatibilityAddress) retTable["playerName"] = playerName retTable["seedName"] = seedName memDomain.wram() if u8(InGame) == 0xAC then retTable["locations"] = generateLocationsChecked() - serialData = generateSerialData() - if serialData ~= nil then - retTable["serial"] = serialData - end end + retTable["deathLink"] = deathlink_send + deathlink_send = false msg = json.encode(retTable).."\n" local ret, error = gbSocket:send(msg) if ret == nil then @@ -197,6 +195,12 @@ function main() receive() if u8(InGame) == 0xAC and u8(APItemAddress) == 0x00 then ItemIndex = u16(APIndex) + if deathlink_rec == true then + wU8(APDeathLinkAddress, 1) + elseif u8(APDeathLinkAddress) == 3 then + wU8(APDeathLinkAddress, 0) + deathlink_send = true + end if ItemsReceived[ItemIndex + 1] ~= nil then wU8(APItemAddress, ItemsReceived[ItemIndex + 1] - 172000000) end @@ -212,7 +216,6 @@ function main() print("Attempting to connect") local client, timeout = server:accept() if timeout == nil then - -- print('Initial Connection Made') curstate = STATE_INITIAL_CONNECTION_MADE gbSocket = client gbSocket:settimeout(0) diff --git a/worlds/pokemon_rb/__init__.py b/worlds/pokemon_rb/__init__.py index 8d31f6633b4c..7d1984d197fc 100644 --- a/worlds/pokemon_rb/__init__.py +++ b/worlds/pokemon_rb/__init__.py @@ -39,9 +39,12 @@ class PokemonRedBlueWorld(World): game = "Pokemon Red and Blue" option_definitions = pokemon_rb_options remote_items = False - data_version = 1 + data_version = 3 + required_client_version = (0, 3, 7) topology_present = False + + item_name_to_id = {name: data.id for name, data in item_table.items()} location_name_to_id = {location.name: location.address for location in location_data if location.type == "Item"} item_name_groups = item_groups @@ -77,8 +80,14 @@ def encode_name(name, t): return encode_text(name, length=8, whitespace="@", safety=True) except KeyError as e: raise KeyError(f"Invalid character(s) in {t} name for player {self.multiworld.player_name[self.player]}") from e - self.trainer_name = encode_name(self.multiworld.trainer_name[self.player].value, "Player") - self.rival_name = encode_name(self.multiworld.rival_name[self.player].value, "Rival") + if self.multiworld.trainer_name[self.player] == "choose_in_game": + self.trainer_name = "choose_in_game" + else: + self.trainer_name = encode_name(self.multiworld.trainer_name[self.player].value, "Player") + if self.multiworld.rival_name[self.player] == "choose_in_game": + self.rival_name = "choose_in_game" + else: + self.rival_name = encode_name(self.multiworld.rival_name[self.player].value, "Rival") if len(self.multiworld.player_name[self.player].encode()) > 16: raise Exception(f"Player name too long for {self.multiworld.get_player_name(self.player)}. Player name cannot exceed 16 bytes for Pokémon Red and Blue.") @@ -100,26 +109,31 @@ def encode_name(name, t): def create_items(self) -> None: start_inventory = self.multiworld.start_inventory[self.player].value.copy() + if self.multiworld.randomize_pokedex[self.player] == "start_with": + start_inventory["Pokedex"] = 1 + self.multiworld.push_precollected(self.create_item("Pokedex")) locations = [location for location in location_data if location.type == "Item"] item_pool = [] for location in locations: - if "Hidden" in location.name and not self.multiworld.randomize_hidden_items[self.player].value: - continue - if "Rock Tunnel B1F" in location.region and not self.multiworld.extra_key_items[self.player].value: - continue - if location.name == "Celadon City - Mansion Lady" and not self.multiworld.tea[self.player].value: + if not location.inclusion(self.multiworld, self.player): continue if location.original_item in self.multiworld.start_inventory[self.player].value and \ location.original_item in item_groups["Unique"]: start_inventory[location.original_item] -= 1 item = self.create_filler() + elif location.original_item is None: + item = self.create_filler() else: item = self.create_item(location.original_item) + if (item.classification == ItemClassification.filler and self.multiworld.random.randint(1, 100) + <= self.multiworld.trap_percentage[self.player].value): + item = self.create_item(self.multiworld.random.choice([item for item in item_table if + item_table[item].classification == ItemClassification.trap])) if location.event: self.multiworld.get_location(location.name, self.player).place_locked_item(item) - elif ("Badge" not in item.name or self.multiworld.badgesanity[self.player].value) and \ - (item.name != "Oak's Parcel" or self.multiworld.old_man[self.player].value != 1): + elif "Badge" not in item.name or self.multiworld.badgesanity[self.player].value: item_pool.append(item) + self.multiworld.random.shuffle(item_pool) self.multiworld.itempool += item_pool @@ -130,13 +144,7 @@ def pre_fill(self) -> None: process_static_pokemon(self) if self.multiworld.old_man[self.player].value == 1: - item = self.create_item("Oak's Parcel") - locations = [] - for location in self.multiworld.get_locations(): - if location.player == self.player and location.item is None and location.can_reach(self.multiworld.state) \ - and location.item_rule(item): - locations.append(location) - self.multiworld.random.choice(locations).place_locked_item(item) + self.multiworld.local_early_items[self.player]["Oak's Parcel"] = 1 if not self.multiworld.badgesanity[self.player].value: self.multiworld.non_local_items[self.player].value -= self.item_name_groups["Badges"] @@ -178,7 +186,7 @@ def pre_fill(self) -> None: if loc.name in self.multiworld.priority_locations[self.player].value: add_item_rule(loc, lambda i: i.advancement) for item in reversed(self.multiworld.itempool): - if item.player == self.player and loc.item_rule(item): + if item.player == self.player and loc.can_fill(self.multiworld.state, item, False): self.multiworld.itempool.remove(item) state = sweep_from_pool(self.multiworld.state, self.multiworld.itempool + unplaced_items) if state.can_reach(loc, "Location", self.player): @@ -239,19 +247,21 @@ def write_spoiler_header(self, spoiler_handle: TextIO): spoiler_handle.write(hm_move + " enabled by: " + (" " * 20)[:20 - len(hm_move)] + badge + "\n") def write_spoiler(self, spoiler_handle): - if self.multiworld.randomize_type_matchup_types[self.player].value or \ - self.multiworld.randomize_type_matchup_type_effectiveness[self.player].value: + if self.multiworld.randomize_type_chart[self.player].value: spoiler_handle.write(f"\n\nType matchups ({self.multiworld.player_name[self.player]}):\n\n") for matchup in self.type_chart: spoiler_handle.write(f"{matchup[0]} deals {matchup[2] * 10}% damage to {matchup[1]}\n") def get_filler_item_name(self) -> str: - return self.multiworld.random.choice([item for item in item_table if item_table[item].classification in - [ItemClassification.filler, ItemClassification.trap] and item not in - item_groups["Vending Machine Drinks"]]) + if self.multiworld.random.randint(1, 100) <= self.multiworld.trap_percentage[self.player].value: + return self.multiworld.random.choice([item for item in item_table if + item_table[item].classification == ItemClassification.trap]) + + return self.multiworld.random.choice([item for item in item_table if item_table[ + item].classification == ItemClassification.filler and item not in item_groups["Vending Machine Drinks"] + + item_groups["Unique"]]) def fill_slot_data(self) -> dict: - # for trackers return { "second_fossil_check_condition": self.multiworld.second_fossil_check_condition[self.player].value, "require_item_finder": self.multiworld.require_item_finder[self.player].value, @@ -268,7 +278,11 @@ def fill_slot_data(self) -> dict: "victory_road_condition": self.multiworld.victory_road_condition[self.player].value, "viridian_gym_condition": self.multiworld.viridian_gym_condition[self.player].value, "free_fly_map": self.fly_map_code, - "extra_badges": self.extra_badges + "extra_badges": self.extra_badges, + "type_chart": self.type_chart, + "randomize_pokedex": self.multiworld.randomize_pokedex[self.player].value, + "trainersanity": self.multiworld.trainersanity[self.player].value, + "death_link": self.multiworld.death_link[self.player].value } diff --git a/worlds/pokemon_rb/basepatch_blue.bsdiff4 b/worlds/pokemon_rb/basepatch_blue.bsdiff4 index 1dd71016867957282d45f6354630dd429e5c573f..dfffd8299b5bd3068e506e6007825b609fbd4807 100644 GIT binary patch literal 36544 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 literal 29579 zcmYJ4Wl$Vlw5|sqVDQ1+-QC^YU4so4+(U2+?(XjH?hxGFA-GF`$jx_d-Fx1u?p|xx z?zemYS@o>m)e@Rg@-i}9Y|My&|B;2_|GfeL=>HQ)7})Z&i|f;9>T|;Jz_n1o1ta#)Z5{w0_@5~O zychuh!1eDyfmagyH-LfypvZ(mY(nym3a4+Ge^yRdo`JoW#G(Oi0v#!_?BF9RK$1#X z0DO35lz-Eg#BApw1^;=W0ILGzQ=S|#c%*zSA?&mz_gD~N8JTc0z%xPxmxZnEysfP* zR00YYR9SeSZ-^LxYyRK;|3qIg!2o$rp7PDpoPrTAmQkg%u(iO0FNSb&aQKp6fM~*% zIk<*62)HJLxqM?$Nd8$^B2)kfpaLl|f+0aLk~069{WBtf6aWCW|5?rx*xI7}&)9z_ zf`4mWB7>ZR=cCxUl9!+2m9bRSd)1zT&N+PZ%4k9)m+_%aNQxyTm6R*E01IWYP)!jf z<^+)P3N8*vONdOKoUk$%0f((H`2~tFH{l9$RJor$bSM&?7r(M`6t9JTk_pXC_K4XC zhg5}gzP>lV+tu}k zTO7$FAMA(}4j?i*01r$dB{_EAc+%==D???7?VK15#4q8KKoF#9iAiCz@vThfSrVnI z!njZNkflPSxo$$90XUN-m&UV{Njb_QGRSMhu*u34an!J&&T(~ch5-C2fXHMc;$tc# zY^9WugZ4OxkROdJP_Z!ExC~m*tE;r=>;(l&F)Vy_%;CUVb#8R5F}jh<54|P@C{An6 zDFvzlFnu0>9@`2Jve|qTC6Ps6 zl{#G-zWhskuCb=-n{d_v4@1E97G<$!?u;yy*7SzX51xqlq})cajiAjs zrBRq3ohhA(aRm-KB)!r;PJ)ZgF>#dR0Vv_dhy;Aa~n6r1=nwM8s{ zF~qZ^*`pc~lgR60{X|LBzX>TX#WWgN80uI~Sxyu!v!%CkhUiR9u2##{h79_3i95oY?(SIbTsl$GwmEQc&C=!?G3-4VIolTU6RuxXhr=BY+Ik3B7`eHWlTFuHj8n+CjZ$S}l?s3cNrms>c!rk)j-n_bST{u?2%!b@vuTNuR6(Mo1Sp=ObwYM3rxC+> z0bg>xQ@-ouZW@nh0-dZoHY7|^6bfmEP>oAmgC|veREH*U$T@kfnNBmPc8JvcMK%2T zahI`)-D5z8W=0y7$pV&9H{*}y8Mn5SEAj!lSPAQ}P{}hhVTs!m000dn1AIeMym>+? z&AkFBAGD7zuIQ7wL&(S!JPJI&*gIvx@WHz~hMajrk3oOV4efFN{+LaUOvRD<3&<|H zrZrh3egliFmaOk6V)z0-i8qA%v^s9-* z^p>NIz8Hu$*~~r}nO)rBj)d6?_Kp+SzDC%h) z^p*jM2lp`YsBOjz;UoX)yRYb$Q_ujOa4u~S@ow+9^miAuFa;~>K*H9Za%Sr#g$>Jb zM_)<5?Rfb#)HKI7RN@1jA?$+zjlxY|pc4o@nfmjk70*#R5>`WQ^p;Lp*ZR z^|ON(x4_b$_^mqyZ&Hb3Z*Y}o4s`cyZ`%zG8N@T!Rrt2sQ-+s*|L{O_WVtUA1=i`e z(otRuq;!QS(=W92FJmRd7P~!jH{;dhJil3)ovyH;wy(~D7h!8)87VN6pqQi@z8U(} zA2X~I+uQ4FG7_n4q#bL-h3MSgbZ;cGrpZu6xk&s_Zk_S!zWuzWJ;JPMp6B@GEAu5U zu6h2<9Fj5OYa9<}CZpKaH^C3oE5+rmN-e2ul%8pyJ{Thw$XTvwf<4=~(!v`*^RLa> zNt>i?uN4i=m8g_$@5}=oE4E3GQEX)g`@M@_RXZD&0!lR}JJwp`-!>e7 za%Y(b)n-Z52R>1k>A?wqoys%)>Yy;>^`+PN!u7(suYJ7BH}1>0bd_oxbIw}?g$g_> zUIrOt0KjIJfsRg&f=ZwymcwQX4Sx{D10(k-h-)6Dj0+HWdv1+hPm*?|uJCNHGU4@! zqiheYyh@L>@3+<;6O9_Cpjuyv#hR`5SEexgGM#UejI$WMU`vW?fiHD~qZkX@KXAIm znNZebZW&&iH+xycw+)-!Dm~_`r>0=l$_$>kIlWs>fLuM+>^VD#8ayerbp$6k1y&#a~FMkAiDzUH8jH}4YcKJ(4lwC?06!R)?@vz*W&6)yB<(IJtX*RiYo zD9=kK>2WyP>Fyn!bPB?u-|{l{+Z|gDf2|ky@yHxjSp8)0kTB@y#XPxr#kBwgkU?_* z5X;E8XtHeQ7ZTu9CkwJ^BpG~?HQ_7IE8k;P>9b;mRc*`9+0=ZvmgnbvV=EpgB}au- z)h{^d%i`%HAA&9%ifNPSeyfLP0x^YpU_Jp zv$7y%iNdtW*s6>9m1irW#I+hxiC4I}p@>8sGCZDn}{3Yr?=nWVrL0J(r!2B4`~0(`RoP!|9KloE5AA}PdTaSkr3r-AZ> z6&_XjA|I~ub5TvLfU*@~+N|<(VQ%G$O;!4yif5tl0^h)ei+><#3qaEp6P6_Vk25X* zgXrICk~mUOBx=rRVdq~~f3Z8sac@ZR9FqRkeyge=APuXzFiIqLm%aS78Z)m zMf^IquOycrcqEUJWQ~Sfelp#vMlg6}-~{W>S8S+oUszjPKfb(vbaz5H{KK-GOX^-P zjkAmMq!AQaJf-sT)yB8qnz!qix7htMU)z#@Pi6q)*zXI76d6xR7UM<)XRUX$sBvj3 z1fFk@>kFi4s!j!seW6{vmcy2>1yQ93;>m*H=22GdJ?(9URTFb1B9fPDO!TU0E1YKo zYp;A1WWqTY2<3h`#g{<;a^>(aS4p*`k?p+NFP$2bz1Ma5 z!{1L>c`A(>BLGc`{j{pos-XE7>EqPuUuD+?zM$ow^QvP02ti(5XWdu)v^F?OQH0n+ zbN9NF-PpuIjbqvwWc)Vj$;~M#wHcc_20OtftZeis#dSg3*PHe2(OiMVq=q-WKnMTM z;lcj?>u87iF!LPDT0UZ3w#Bbhs(LMDF(fQ>0%BhkR4>?sArZ2Re@>Gl($|_|AZH_7 z`4~%)UnE~e&t7VlwrX$ED$o7tfjGIpDiHo+lN(+mqyA-qz8&Y~f0$^IOuA?O6KIrj zcbO^NAN} zJPAjc(tk?K^URwQXN>0Y!m}4b-}Sf8|4h%I`I z=9z9sS!*c&_9anxZ8&dquR{B9!Lx~Vdi68OHN*OQR^K-dWuN)ZfA?d1@3ZIg7_BtbSha!A&$0-USVErD zrnUF-ASBX;+pX#zR6#{zW^}ZyUO3E0n)bJDvobT2M6k?!pc4F;KH|S4kZA}SOAiXH zNQb@?w=Z|bNUR(I)g30yJctml%UmH02q5!TNE5gfM`AqI690zIPTe_WLhnVC9um!~ zIrTEwe>T0S_~Ef$n4?EJKSE8Hz+ZOIhJv=W4fsT6U=#d+Ck0540vxkY;YjEyUX2p4 zr;nm+{`pPFWDO)f0d2KZekYU_^PW^St)Y0{`(~&;M-iIgwGZ|$oeE2aOwqHW0KHmj zY5?u@mVRzjCmsuGXt-3ZtDfUrM?)=x%2oml9SoG=^^V|rNI>Ip-87pW1 zcb=!y;!liern%%44*V5px`O?YKjDNiGT313StkzUjn#r5hld>~I&8FsS~~IwH#-%m z8{LOW+@`zORB=Cb2{!s7y_;g~m>4L-3(eNcJl)pGB=K^RY7ymCHdDTX9^_z)Rs4g? zvHmCxk_x!KYwJ}h<}@_GNpYnK(#$JAD5FkJg3fbC*Z(!S0^;Dr?j_HV#G3)&AWP1; zrcTsSu52KOqdlKh+Jv2OY!%T6M;K(9T^DK?TS^{Xt4kttg}S|>QmOpXD9QZ6F4?r) zk{Q>Oc&*-;IZ_k9oBeC)ZJ8_1o%k8HZOoFxw@xVky38QzK1t$k%JY_bvsPr#cg8Z3 zjVqqN(jN#3YYf#xJWvj>JSPX)Nmv2wBsVHb^_l$iim5XVBb>w06Rc8rQ5|OhJJ+~B zH;NekRyh3$kFIi}9|brrsc=vW*5|EgoJ>P5=`JF{d6D9<9Xvw{KZ1v@^&7|_5MI?XN^9mp(Ol=8kX2tZgU^#dBA4dSQlV;s=k= zl%-5?vU3ld8|4URFcboga#u>|_mX=!x6Q9!%b)OlKauCR43yX(T`94r&z#Nb52cV% zBqJbHTJ!V4>S=lyuU=O{*ridojSs(RQERPo$YX5FH8|`1@LJCbMKuzJ=Tp%Z=00hC ztoa~@&Qf1}`XmlpQ*CFX9{9XU=8t}%X80tSil4Z_5#nSRmgg-uHza!^gf%C|UcN^2 z4kJRJE{C95$ef8b76v)YJ=H3}zM4T!adk79ubt`D;^gM!~smKE9=K}ynR43UX8q-5)qxfj3xt84> zfud9c0n%UNtJM~WG=%SFuQvk5kch4MYWm9VmM7RV_7T~r2gAful1|B$X$r1>9-!0U zfAIDwa{s)}3EkMNFNyhq*hAUw1)lv0_VHX|KosVRh*lT##-ld8Q=!*+MXOhD(h5N98(?Z8EV$N!ybof>-e!rUueEY zfHU8c)KL^yY9`Vi(64){SuAY_{E^j_)Q-fmLL5WdkTQo8`d-9hOmHYWQMhx}dk?jV z#t@WYU(aQFvV!ifiQJQ_>V8)Yk_U+!$;W}=;fnezp*6qlCY(++u>brMl-2R*u9T{Q$e-@O7NE>i=5Yj_(%Hoj4g+#+OGw zrp0tV;yYRwtX0=)`GkMsAnZ{0Q@WrWhUbIhFg?8maD23!PIxXLojZMrhw7nuG*nu< z%fdynqfWA2X7V(fvVURj9y^(NwViJo6IdFw=J1L=h zjC$le-A2o*C6Pz6uEq`<8jQ>GBbdr2oH^CP8_KkzX3EY?@mOM}F%4W*Pb-N|3%RmA zyC-5u+rG&lxb>0z(vK;$7}lbrmLkVFUBETBQY2f2Q{EksCpI{_s$%v|+#fX&*uD)V zS!CWTlljX3^Dnml1$iS_Kt}EAZG`EEmcY(rYOF@SlPFST$zkS7P-bE_&c6{sFfgDhI#QFY#kIgl4tFX%LK?_ujnMrt3;tf zbCMh_ys-|nAGWf%vZe^eOvt7wJ~RXb0$$MOb<+*-dNa`&Of|~Pe*Niv99OE*4rZRP zKq?ozb5o^u`D}3KhToDOXtpD>_}=pJNx{U1I42qX<^Hd%O2u#4p;8D z@(nXOd(DiNff|8~tMLHXW0@^jL8GbfpoeDjkPb6Nd5KSg>+-Bw79c-gSp=;`|pqZn<h{_f zl;7HWB{YPfzF<`M6nB|o$%Szx;*}B;e_p~p&)Gci=(%nHm(Y}rWSirSIe}xMdPB6? zrK{cLB11$B5-AU%;qeSQ#PWo%)j5+(^6C?!8wSFdz{|~7YzTs z^2$P>=Vzba8JfWj34%OW==J2sintDRcG2pLNT^v;yFtr15+a+|7R_g7z834(*X^hY zf%%Q%#J&7LW4a%8JQ7g`u~_a=$0pi1Gl*tAT9ZsU9j!Te7xE3}pnE@*3TKAyx7vpJ zt!gBQe|243`B!BQ`)BtxlYBR$YZ&WR^zVs)g(OLNrRk(ryfPehFfp4HklDH)-WHp) zGVNf`)kr5J$L%2Lm1$v$;)L2EAj1&bT%aa$|8wr)3? zt14>Ypmj+P`jXqgDF&J6w{L_vpzT;6-`7>qb*Y?5N@RS;MX=58OkkFSpgKKfVUS<@ zqVt=GhmhB4?qU}YH!{R2mS^#@MTg!yXOttVQZJEWb0a!RTyvKHc#Fe?h^JFoTM#Sh zIWjU3-eD(oH6O)ZX^!CndAUP)V-~MoUI7@wQ)AF=aIKc4Z|5alRgCg}g8nO5E?Pge zgwg6`@KmP0+S3@x%Kyo6g?Q?~DVhJV)h8x0ii$SlQ^%5GuhvPQ&tGU8VF1*gcW=I| zVMGPN-z|NDw)0pV&{WbD^C)+gdI-Uz;ODYWd97i0ZiufjD;crKWGnS}CDDbYvG%Fm zDd};(FIHLWTV=V6zZj;u#{7~!zwAymXtwF)cvPT^324Iy4o=Sugxt$Ijj>DW#c!jUrv-$W}ixYWRSa`o2aS!^%ij#il^%?g@!Yk{}-3xT<) zCvevV4Bk=qS16T!*YXPss35!u=_EPjH5rH;^vKL(_IF%YyQ=ikgJOkO4O|V!zFl-UP$Ti&+r2r$5aB1< z#{!b&mhzUkW0tbe<$ABFK>`fXZh&Q=BMBAxg&eB|2?Q_!bvu;fIjVLekqLbP5J`f` z((<12cG@l{97#ljg_;8pD%>KKTdVn$?mpkS1WFwtp(?mZ*uhjuWBH(z+hTwc#f})N z+{B8Y+IdA{Wi6iS>Dn5ggnBLLU=)xd+C#Qg`$Mc@;R>WohP0v!e@@(aHDbe@7Gfw5 zg`>mq^x2QjIt&Uyq)E1`SPJiHT?=t1gaZ(%S`fj2OuCoP7Wz_EXAH13$D;|9W76D) zKi^80{V23g04${@9A2M{@hTHi=9g9DEgflOJ%wv4+IScGMc(ZKk&1F338YCdk1dgy zfX2%!Jq zDq>E$P|FMfP&Fa{-Ce<6S}ekfcYcUI$!S3i%$6K{?;ffd;%L3U*uFZ-_Hm+iSx};% z42p)7(3f0}$&YaqMiv%o?oS?s8jIzaL8G+T9^kFXz+ z&BM*~_##lSb{leus-Vv$TILC_TE)e*d8(Kq@^XdfF&Cnm7fd{Lc^Pp48ZDS(ZSgX zGn26D7TpAwYSs^Ya(Xp1!BfbVj2Al)Ydp_>_9xDf8BU|2WTeC*I|&Ajibw^VR611# zI)Op-Y3j?)pAaKvj3LbEf}(`7(02%j!QS|Evj4=nErym>6@PYQc=}a-blO2mDhf0I z3f?P@D~EaL;ja}*+u>FD+m9zR%a}#>I_Y;!vwN^uF#VwtA4WysS4I!pM(EQ@s9lJd z=o=<^rH&GVL3JJZACCJRzhj(6*@^HcATZ8i7;ONuPLp;Md-}a7Nkh4h8}3*4+Ud#R z>#(n7Y2xwd8cTso0!kMA;k6u5S2&g8@4O`u9pdBUc0#c{s9MP%KQpXnq17^hKY*zA z%qCx?Gg`-ZBGjTOgpcxtjPPtGm6C!K7{A)b)9)1wq1BCuOBRm} z>~cIj+Acmmy9BQ1{wr-~7Udxm<2-6WOe^mW1}gsmr2fc1aG^=?2S-Y+I&GL@BA2() zwkJ)0axcJb+|EJdOW9dmLS8|~M6TJd&mD*|oQ#*O0D?sEqtA2|j=oaY1l{OW_|$b@ zbfK#>ul$?A(xQZPXbW;rcWlz9=0~`v>#*Uozr{j#iIPGc3P|h@CyL#nm*yTj(zbou zu!VtDp3OuU%Cbgk`Xh#IkCqzNG$_CdXYPPX{wVnhgUB+n9ovb$3B%d#-Z3e*a0O1T za4-E|X+FH8d;1`*QmrjODr<|gv3iJO`t@kaZI`e&wkD)D-BcGsQ)of|HFB#Bg1 z+2k&(u)fQ-5siUHQ|(3LlD<2>jjxCn!zRAH-kZdp5+6% z(Mq7vnr3=BY1~}?2*p^N_=IZ|lOn|`Df!L>zWQOXgspQ(J_Bq@H$9vbR*@o}u?o(0 zT98HTAQ(5U83j*#uXglXiurt_4xLs*FpHQ>C?ypaj&kaQ#&3(_Y}vWj%f5ha>P zFXC$3noQj08ZLf-0>dNXerc7IUCBlCgH*4FFd!H!1p^T_l`}<)`rba-xN;dUC0xA_a$)wcGbMWE~rBie2dLUQ0Y<+o|-5;5uGzhS*VI z-#WoJmA91>^YHmm;}>iWxRow?uag=17Jv5q=wWg8n^=Q|9`^1KY-7+X*Ry9}7j(#3 z^kr6cqX+>=pnNW&^OYi#J^}gSmY`UTCwI;GWGB<|5OU8OqVgC*cr&Xi1Z9k`aGYzreb#Zy` z;Ry-}q;Cue=u+ZXGtdrHu*xo7?XX{io zi#Ocg`kcmDu*Ze)o9F1+Vv;yjx=zE-X_6b9H#U`Hm~O}*=7R8_Va(E&(8JUVoT5|c zq;M>QVFty)MG3+^tbBosZgoGyty?wiMSevm4kgSliV1}V538gzA`gn+t*nYlS5Fb&)QHcKTE7?pLVX!X&&j#JlWzu_sW%TU5pP010vrws5mq1uyiTS>CT3Ka;;qo}OP zNnN_62PMn;M$Z;``r;zXec|My2Xb#{m^g#W`#lX-DivQl$e!iBuv zRAaI*L9d|&4O*GLH3A7Czdv%(ER>id z!@ztakwBMnQxk!!D&o0-LGkB&Rl&i=YO}1u_v?kQSHlmtlEu({=YiLTN4m5b6A_Dt zkbP58RD0Bn0^dci!jl>Wna~o7jH0Rvq%vxhX=jU+MQE}_N@Zk3U1>@WkNLa(o6|52 z39X!(4iPs=Ru>-v%$BQ$UldS59SMum9d>YZY6~thN(ddGdK>EHgbD%k1dc+@*W&VGiXQ@}_&3{I zL#N7svodTKxCQ@~Fd!Wqy%%$Y_cJQj03*Ey;^ASRf)qwsD%^VVJ^#FK$_CAcJvm5b zIwV)^I4cEBVVN2v2W~<~4!T|*qFkWk2Q~tm5KR^u24Td(S7cNeEe*43fhx;jnn&YK zx8*~}EOr|oLl~y-?S(>4Obpq0MpC(a)!RQ5y3rN8TmYZ=q7)!EZqZnoNjDNfYJW^M zWhi=sa~flep`f%}uh*K=D2wr25lKADsl?_L7L6i2tWee>46Hj-7xy;!**y^$kHZ7spe zpYLg*+QGw+?^JBzS8zdED3q`$-~~AC@Y$~nTiv_tt)J8TbZ#7F0@Tw{zrujGLghrL zVsKu5NWmo}zBi7=F5gaj+%X;GvtVKJ=evQwOZX%b=Cquf2v5zS`x0O+BQ(^}=( zY4N%VMJ4H)ypHI#^8LNCfxgr|8scKgl!`~z;8;527!JC)g+R;4@q+RO zfSI)INA^0S9N&&VY9zWfC~j^ul=#ew&q;APTFoWU)+)+{_Ctnm@iNy2CnG`A6o)Sw z7d4CX)kCG}`QcENWYdF^l=B&Opme%B)l7nuFl!0=7SxMSZZVG}P^nCk1qA`bj;=7ZRs#u~Z>ZeurV*D2cKP_RapPZxlD@Q_=MW_l>0!cmebiE0%DiW0_`g`dU z*g2V&7r4Poo70d?1xT=k0cJAi%zP%I1VK(lc;L5n`Op-0ES(;{iz3S?B#EF%U}cqU zJwr_v9b?&k=mntcAPzetE0s7NPKLrMNn};Mn5rk%R^iBDvVx4GVK9_~k^_~achcXL zJP^j5ECpqs00}NrB8!ghQp_9$j3~!Xp0qfvk-|#^c2$5x2S*zyXwalZY$_1HadQE! z2_d*tz;Nrxa(}k8=+X>yZY0ZMX(@g(%PI|atPFgfhN+~ajBk>$=4!u)TxtGw^VIy| zBW_!AT4n5>^E(UL5;Zc~+;R+wkT9?~SQ5u$f0% z1($JKVNj(>hRY|`aM@K3Skrw9kA;vG)WpO@D^;owBcZq93|c8vI;!ho;fm3z5ZW$$ z=Z3GX=17c=4lT)vCK?)|uf>;9TB!k5A>mauHDhvu$Vvzb=?ak`NCpOU&}q>)6~)w( zh0Ayn+VF2tO7`pw1yu~uTGK|*!C8Qciz%k5U9oGitl&g_iq_b`W42Yy;{Ut7xkhAn*OGBVMvH*EoB$B;=jUHi;m0N_eyxb)l@_19Dq7$^85cWjwt3K~OUuPVV?aGO`DtBT zbP(}X;2%cYT9%*?9S@)R#p;H-MdP26gogaCkaW_crV9gVJjq2Y$nJ~EUiYPLMpFHp zCmMlcM>{E}NTJgzB><3~k%B>z+Sc#GimBvMQbQg`#OWowTefWrRe!@;+9WK*v!bmhBO$R?5iYbR(P;HM!Q?JSsXD zvl^zHIK_gNKr(qSIT_lVgwJADehBSOtzR;j1Ko*7Z3GIP1bR}T00O}b{bK7%2OUh1 zSXej#1Ehy7EhL~rLN8FTr6)?bG)U#8B`@G@qM(-@51}Bhf=+V7!dI}MQILiYEFm?B zEK#EMLDTU-Dw!+CSClY4Tkk>kqj@WV}1gU)WQ_>mS{bjWJ!PJ1d*|ty_TsP>L4J zQ~kOcEwVyE66|;Q@<^#{ZM>{oq^4#_(2cS`{+&ZQjg_b(`poH%M?xHX(5R5Z$2SMx=D5KJSJXd1Q z6p0@{c0JS)h6Vvzk!7!e9t}VYo4PHL6p99cxV}O3V|@D(F;UZ@1~jjh<2i}>650=TaSrRbdmH9hU+u0NB_ z_P+;oxWR&z`0wnFL&&I+`MXD6-XcoX=pdh7RO%0_Upb~4H&^F8NslBHHcINW7wX^zX zF)=1{``|DNzUK6Vo6m|1j)9in>xR?|ikwhApGbr{0fMnDla!K7b{u2I5&Shg&=8oZQ`z21T9fX99^tVGfj79e_Jak!hHFqFX zs4vr&r;|WmE|KBS?6!p%J6>`age=De9fDk*Z2vKqF2NF}2nQ||VpiOgM3EjCx5$af zbZ=Ty zp9x?1me3t4Ir}yZ|kNqO`)xcy87ttab?Wl0JF*8KX7zomUL>*r1E!VHqf#F zbZkt~HOR~Uqd$BhahS$Y@9}1bxOKuamJ7KUfttNAdZ`<|AKR0T7sJHGQmBIxeq2eGS{HB5l1 zjm=lso2TJ~uHfFf={>vSUxeyHUi>Mb>4IoRDC-XJnNzGXPI?+ezKBRu$DruAlb@d`V%mt%|lS!X$i69i^|Y&dUAG-_?(=IJ z5wO4ga?x$4CfII=DuwRXj%^VvE^r6^GXts=Ooif)RglH@Gi^x_1tuN7SXR${N+?vy z_ouUI_~}hR_aEk64{l{@Dp_$db@Z!{AQq6;ig=Q6wtA1uxLHNg*wC_>?KBgI(n30| z-84B#D!QQhj^71Qq);v+Y(?tKZcTIb$D4Ih3bv6}LJXtyOLCHAM0;(72)uI>{t>yO zgjjVLT53YgM65Uh`~?+A4uB#mo9!|h9x;={7Bz|RzCn_93fhLU3ypN)w+MVO6GUV{ zu#8MqDTAq-UgswJ99K1kw2 z1bcy&9Gn$|&Y+e`r*6;H<|CRnPFYr@Mg}Yw8388>gN=n?{eY6EeYzigzkk+{AUxA~ z>|y7U1As1+4^t;y?2a?rPL4HAmZLldf}c@d+j_P0$`Q4UI>=5W@xuAzr+(9-%(fQ? z`n-1E8WPBHr;Op`m#0PT!c0_D5}Nk^a`{DZ=1V5c=hx4rD49VL7Z+ct8oFpPR!U0oCjMYVOupNQKU**$z?dvC zfJ6hM2cqHS?Ct}WMwZgGuU`U)qwxhYoQ33pj@X!qxXt{46nN9DSgq)PAD9%hzK$YH z{)H!D%K7bw{&g7nDXnm?ewQm;tSzFlS7&ab0WEx(wCMRNV!e6MXZwx8zx>l^*JA}0 z@G@@N-=^00?F?QPDYv5$kBKZvxx=t`g+Bqg%wZ&f{M!LHP}R0cM)pQD%Y=qb>DcMM z?!M?)qA0B!H2Z*Jp;)Vq1GA4#B$hg*7M!=b>g{`AWsGzhPEd^1EU?Qq1Vt*_&3ZS) z$AkggyliOiSuU*FaLWI*QBk2jQp5{4Ak#8+oco8+l*vSsnqPG4lwRO2yC(9~@Qvo_ z()8nK{>S;PvIDG{+?wct39~4cV1qIV|4oPbP%9A(#fDybVPc)`lHuxE7aG^ePtKAD zQcjVhks+^flX%|qGRS_@+xzNwY*H`%_9k&?w4XT9T1Z%%oApZI&pyvz9e_Js&x}eUPyj-27 zZ4q|J$~E+M@A=jAg3N9INJvHfC%Iq#f}WXr`%)g-IqvI zv9i!=r!F}Imli|$fhVURrP=k>i8cnTPhRs2cv34bA{}q+n^l?kJtHu#h{cd&BwjtU z=d+{lm4cM@B9ZUN+0 zL?mC6lmnu5>+DUQpr1DBSp|_>Inj!P5E84w&paBkgf(m?@*hQ?`Ikq*uuI}F)U!=N z-rrJJSC+VLB$BNzI^bQ4l-X`9P@(nfX7?W`WAw~yiP0Dsl)e*tg;!IFoN#_1CrkGZ zyB#ZE-qlZ?`IhbPHRFK+ikVT~mPL53tymt$z0WkJQEt~V~1-n`;}r|_~f7JL7bxC81i*N%mNhYy>t!$}bh?;X=*O%_@cm8(b*oj32A& zjuY&|&)5?R(2=>CXDL6A0gSa$k2eh?VmtgeFzd{-ekFbq(~Xw#uGG?!R+o3}AmBN)CEL-QOvlUPY9~2Lk2-3TmR0|>&lPDidTlJ2#_^2g=G81Yv9Fr=Z(UK1&M2WF z%Pt>zzV_F{Z(2T|t=`t_a;F;`dduq_PyIh>wMmCTkpyKhYVmUyZb%0aNdX4Y&^UdS23^e(w#j$0mw}iH@fSR~W-IRut^237|qcll4=C=hZ=B~oR7aLuRe~R`J}h<8iCiv%Z^>WQwMwc}`l2@1O{I z7+;1cLT6!B4isBqp@N2t3pVX@@NUK@A2wHR@+ird-Tmf_j4?#Ws2nW$i42Df^x5wd zjfC=E``G&W0Mf{$-Owlt84@!UC@Lv@^jx5th~I14Po>gCfgjd{`p*dw0D=Y0(3y-m z&Uz;Pj2W6)k+Tx~NwXtD3nGF@Z69`rs#>L~EI?Wfo?JI?O2~$4R@R0HkB#?VVe>H~ zn2%QMvMa$k@0QRi`dq-`EG}4HlHfrA46SES7mR`Zypu`yrK~{dNwc*g=*V=EL9#E{ zRwpasq2Use>Zz3+W|frX!gT9*M1k3L{kZ&o(!2im6<>SP-}}EurQ&;u|6<|@%Mre3 z(aD=Zwx9vXy%Lg<9=5AY^^~cG;;}(D`qT_>^rkb>I0s~kVP?nxCb_MFjZ9Ykdbq1`)47xFd7b<9yJl`2FE2#o~*&GooE zr$&9hB|xOO>>Vy89-Q~kU&-qQ` ztxg70#PG5knQRLj%c(oq#~GMxajQ6B@t>pj{$GD(ZFqdmh2to8WC{k5Sx9oFFx)!O z3+T{I{k1q7zja*RCNlQy=zj-Ro{m6rqrsPh7m5OLFh0AtU34TmeS52k*ZK~ zNFa&NV&_Qa!LrC6W%-R$iCe0J)RY%UamHiUUJnTk#8+1%*!B#ca#^>V5@~KHjmy@! zS$P)YbKvt(MYt9S!3WK|jie*M>m7w2b=(iG!l4{Tz-n4-w-r`P-fg@;EHrkOK7x)D zbR2FGFDC1dvYdlVG;GiUli^QpS>UgQ^@BMWwB`F?tlSU29 zH#GEU=Q@3Ol#N1wOjEccK!gY$P$Fs!z;0&%aDe%6<$q@$Zmi?EfgKn*Eq$tZwo-_n zzGwa4i-M8ZlTOW>bIT!+J7{zg`ktS68NXHb-SX$0kcO6-Hab**!r2Q=^qluZ02@m} zobn-*N*tjx#>^#}MtCvEXopfsO3^^m3VC#3&WKw)Uk=OBsU;_yzgaLody_O`><#oj z!Tl5aCjmhKP!d08ou;Atgszn_U=e#QZJ~MdLfiGU%)!yQp7x8n2bjKmF9L!8ffvf! z^z*-mENcC}{0ik%YF_@wrN?>l%M|P+wiSpvO36zaSr!gmOzYH_myT}YyVc@=ut=3p zA2ICKw3aH`(1MB8!OJVPYJUTQfJ8@A!F9@_jo&WjI~`6 zYZ|x2d{^J=SJYlvrGnD7eNH9qcQ94bkLATJ19K7^;QUoBVLu_ zED;PjNJWGYDsGT%hhe0lLMr24Ro6lHaprPTMYx^v(QO+DnnkS|qLmOU>nA@GB;2HX z=}n!|G}Gs0AE&C~AZj3#edXdI_vda*7ZdE^sp#v@VVV35gB75#9-x81K>cr>Ky@B* z85uYDC~u|5z+^(BjOqsl(xGXA87O4Z*%W!%{SFE`hJf^Wbn5Et=-6RPP4`O`*YYY9 zHib}k6X!nXT~GO-cfQS_Ia%M}t!APpC<%RVzTr^;KUll|QMJ>$Se*K-HsxK4rfAAM zg!Ud?S=#jTtpt15_FVY;#POX>;%O#_Rgr2=7Gn8skp6C)xxN3})%v`lWI!DHlB^&p z08wfO44O{Wtt~|%N=!pR;*`ack@y|pokKw%1fZ0$0ZL>ySySXM&-uV@4EGqJWXEETUr#Fz(g15h#48tNiC__81D?tEcU`Ro0Ybr6UVWl$5Pi~QGFKtE zJ7!b~G)x~FOcmvC8@D3X*qna922!k1V0`4rf#<>xhD~QbFLRG1%leU65){3u(R3%l zAGGJI$q*pgpipf-WeWZ>dj^c;%FF~IyK;xhy~o8+cdB78WpVlyXJJFfp*G+~jYuU> zl z?*12k^f|ThsFKFojTJ>UR1IQKKQj1t+`SAxD-Q`iQ}O?|py`S7DL;J_PDWex04W38cg0*>8}VK=|pfMQKlOVBZ3amhdY z{*vxKk5|3FR4o|k`$aZWtV1dC>K(+K-HQBMJ3f^b<4IjqDV}N_7wJ2YJ5%i76ZUs& zmN9uSd%OCQhlT@q;UnWfon)9m*2(gj0{6swm%oaTSv70*ofygpDAiR#631&W9YFqa z11D(s+u}cp_Wc^#z2+GeM3Z@+P4N8fx1jLB@SuF}m)z<$i$m!?)4q~qz=lao9O$&M z3iq!Ie=#~7)$y2dC$NUGA*A`038gT=!xc%@XtIKuXcb?YhjE1HduoSEMQKeeew9_% z8WtZ#+S-2+q!Xr}Y=!Qi*>yFc3TK%9K_|y>ozz3g;MCL|%rT|WdTD+?>s8we6#uiqdx7Tsk=w}0J zTIS`;d{2m(VosW;{c=wk>v6@FLF?E5DypV zFnn@ky6|i0aN%^rv{F4VFQNZzJ!{B>`4A_5@Ak952&VMfjgtB7-CNNSE|w$CC>&6G zzjJ%daKuecWEpV@7z~jQyu<2|>rag+^u5c1X%v>^84`6{Np#)Ro}ZWbHW7QgFUXZ|R&i zNE!qV7Hr)?k_NdSkq$!b6-wldCvX1z`awQd3>y5T78dOoZ~_P|!Y-W;*Gsh4*kC1S(`3Pl{1wtC>zXjiLsEG6P}s;#;JH9r7WoO} zdNrGpixNXp1YHT%_ujUQJGivK7Ld2oLx2&{Q`VPe8MYR`oUPkKvRV!Ielgoof;x~| zGE7VN6w+o18UYR0$eYS`YnqGi$K3s`#@Itij_phgD}=93f2)H@AH$i*I)n&RUDb6EPOZpaI6Uz_7SQ zCI?4Ob#2{QPK@is?_~nmXhWB;M4Rk}&0$h(btj|t8QdNj5)rCE9$+G0@F)q`SJ$z_ z?SAPNcy1d6dB*t{=2I6$RPE*pn-g$m`{VntNeE1Rnu*91{|co}m6O0AbBIp42xQOu zJcxe@{?E;=sCej4owlU#v*M?14xSBReNQl6sWCw5+L>8@O zymJl((~=AXmTZqXZrOoqRQM*^3)4BQCLNyg1AMHDdSp)camyoK>ehS0zT&2sj9Yd2 zPN51Ky1ESH&QdY~A}&hYx+(cw|7nTa&t;R~u%YX)q>Bwm@YL!dhCyM(nRh%(2QPhO z>oW7H1E+O-^bgfhhb40MbN$X6vRmf&b&uoUnv60c+f?dOKtQ&b+@=_GT{RCChO+I% zM}2-DJKs8v@zENKi6bI3okR*81J9`edybE_*D^)TE{K00?V|*0JSdT|X6=wQ_z;7j zf^#}KzM;hhB6_78i?%KmJ1Qn3p-IWE>x2>~HW3&Mp=#IVxxmbo6m$ElmM3MVMaMN? zCu(@NH~&uTxv3d*E3>-GBJl`HX$lDx<51$U%FrVoDOwhLDYH595aI`7EW3MVgbK3~ zM(tyv^icsrCChvw1QA{S8o(H2G^Tn@b#oV&79~of^QpU$HD5O=MC^TiQzZvsSCLx8 zLb_HOgiHk` zD2BDfaS49X+HHKCjl(Z~$6(?_VT=ZnxWE@jrD& zauY_>O1&P35BxpI37>5rLvZ_w7w6S7IWyJ$bo(voy%^%_ugX#M5IN_oM}UdijlD7 z^~5vE8q1c!)?~QJSc*lU`0me4ADTrkzYx%U*SycGS1!7V>r=>w&pbN|&IgJRNoGLb zvXl{{K<8mJ||GBIPV-T8IUfnQdnaRU;sMvXw?8d2q0>^4EyR ze<>3dC{_c+*ii;VLyGI!-76Y_fx<}iQt8%VPKHEY%64DoHJou2}dK$6} zrU^2T$N!n=IuL`y*RyJI;*LZN{}b%rdspb`UU46L2EsonI%Q!PPpS`EX<9xt2JY>uHROaRvh~0X8K6vZPrkUqMgOA{D zAKAl8u#*haU6ouX@>0D)!nt7b5UyuEd4U>eJ`=}ms2_FpQxRN+b>UK?kHQ6mgvL*? zuAsi(vGP5cff=FB>mrBAblot>4QHoPav($HGmL7YnsM6dX;+(%Qw7nH$rU+1!{(2T zUubX#53_wZ8q9E|H5nypVP8iAefz*VWgE2{-XA$S%%?cH5H!dJhJq#(`_*g+zXsXT zz5q?o9i{X}kW2PTUV~+a&@scv)pOCt&_nh8ZrzCZ*%|UyNq1dTEn~j_EiP6p%J5?I zvdf#g9a>nS*ay+MJ0W)@Q-uizc?gn_LRx4!F+o`-Q&~J$!N>p$MSuVQ|NsC0|NsC0 z|NsC0|NsC0|NsC0|NsC0|NsC0|NsBs7XLcz8G6(meeUpkOw_08il2!xdSW!uil3@}Q^`CuDe8F`3ZLjkLjq+p zO)2?7KUDo08f2jmfSN`E7@ADUP3mT+rX*&Q(WZk5 z)L?39fdQwWgHs7H00@Idf-oUZWGUd)Hq|{fHb~RiBml;s2dRi{O$Vv!Hd8H+Bh02u}l00000(9kp*Xahiq%4n0qnx0YUO*968X`pBZgC;=HkO!#I>HrM`Mw&EY z0LU~3ng9S8Km#BKfCE4p0009+Ks0DH(g~<00U9!yp$(xiFoR7KF%u05=^ImOo|>Mg zriOunXf-yBhKTh&G=oo5OidXvF*IqWnrV|pfCh~;4^SEz15Gs0Gyu@ju9@t$jlG7W z73C}Bup%ayfR)GpJSzQ!L>{fp$afA9+GWMN<2#g;wbtN}NI9O%XSM<=?ZMBNAwq~k zf+NJV+2(6|kIByR9$F?sVmkSL*HzT1A6H*a=HP_%Soy6&!@M51IHLE_(rsc(e#n1}n{kOLaNAk_`?s2$J2PM3s%h*>X<~n{F!x`NehBZ4STqMQ**IzIF8kdK?E11klENG_Y;9KT9Ypj4j&hX{*UO zq4%hpq&qx@gDg8*#+e^7HL|E2$oeAKbDh z1ecG1qAc*XYYt}OWfGcQWvs1ZA3n8<;wN`;0~x<+Xu-N>^gx=Y_EHp_h7-zHENAj@_c|0{%Ef9b zzGE@EY;mj_t%NNo3$tck?rWF8^!bfkAYW25WUHOGU|>7K<|6PG#AcM*+OnM4QS-Za zh9!QU-FRIMbb074Q1ft;lROknbMu|!baMl3v%lvgaFU79PZhrnk>)U#b%s^`U%j-1 zYHK&ur>dqp%{B(aQ5ZEXE2#dSFqAZas!pjCHCEI>xCH_f{oK}p{|Ly$Jv}p7b!kfF zgK;+;>JVXobE$@^J`!823f+(v=0Ky8NE)l7gIufN?HiYv%N#@2N*_m?FV8{9TzIj( zRz`-OL43DY%T-sJ!A4~qz{4#IMNC^Tq#_K~Rr2*{%9F26(toFgSDQA;I}>2-TFTpa zNGAkM6DC`$oI9-euN zA=mKeT^PJUbT;Os#Ch5_bke_+_dkG{M}$l?ev0C!OF*D*oQm*|KoShVMWRShGwCBx z(e6$xTPL8{LXPf?HW)N;=mtxcmrDUk3P3D%;n3LFA!rB!VI`z?8?)b(Ua-^6A?K4@ zPHZGUMCTM5X9mjjN2EtI9O9qt!SU(XzXxHau`oUss25(JxcL1d?t1euI8S$!J<_bHzI+ zYIG0MSQ)x8?E=y571`|WsBAHCy|XmNsUz|6A0_rO;Soa4IZxB09V6Yx(QnmtS|N62 z6wDqyBXynaIv9Lmomm~`YsT=V5yFW`V;FIXR=JvNO;vWgmw8{(*3S)PLS{G!t4&jU z&K0aIzwaO&1zOb^d#hTi>pai0(gK9AU`&P}kfkNwl}G76kpeV8>C~6^RW|zUIUr8Z zn=_S!aR}+J33!QMUIsMf6!P_dWFna)z^QR05)@!_)g>H8P_^|+VV=X`1no2ipN0wp zFc5z-A1DBI=!gi(EvBZEDlX?Y;o!!N-XJ-}?RXLUop90WFaCUB0)As* zM%rjmq`mLR#TepPxo&jE;gG*H9aiT0tlFvTG}a_+IX1JQC<#;25cb7NKXiw_SzTU7 z9W}H#x(fH)W5r8-sKMlUgI1m{?gCAQmjdI2=TCy>@>!g_tFm*%L?IYgrCvurvb=-? zgXBh#%uWiwN0HXn*NI*7l0gU%l28&s2dJ|dq_=Vb1Re*@4CwmB?usG`MJDL~vjn`p zPf3T1GVVhigfWG&S`U~6&bw-Q{jJw%lKsuT*MywBm1R=LnHC;4NzjwS$TlG7f42tN zNPg^bsyViIZ-O9tr8OfQ(Uuislc_FaE2g8mG&3X8rH(aJEK*hSt#SPn88UytW+*FaEk|w>y zY<`5*&)(j^m57q_il_THFTFJKj?AH0I1rc^(2$vv5F(FJ2Z*q7kEczLg%hZ7< z0}{VK>d!0db!c%bwcHokM!Bv3hi5tsAP;_kPuHw4k>Y~Df`Rjjz^Bx}=Yi2fm8mm7 z1F%lVU0|^PHOGKHX`>!0@yAK zwZ7*zUKVUGcJnP6tsnqp)ptQG#>7I}sFX_W{dIM%UP@$F4c1;VL$UpyVtyD`x7Rl; zT}EgHDe24G24z-cTAehUeeKn8_|t2-{Px4`E__t|ZAL%Yr}ctJ4D9Hl{8`|FrMOx+wQowfj7htUwOw9ul!xw z4_c&vr2v_k5!#Lpw5KPcQ6xs!_*=;l`$O81t0Q7WvVpI}U~xzJiU-noQUL{4D9VM6 zU|7oMIT##62x*A4xWL3ii%T56Zc5SZMx#$QodJbrfcLEY`k5M0ZnWsc&EztU@6-Mn zqTQ#HO68RqWT( zh{FKE))-GEBMvjXoS(_^a~hY>pnr2w^NFN>h^x1Bk<4@FB9EO6{7Nt1NT6zpRSu2=4 z_^n%9ffPj&FoaKzE_BwylFM9`?(Fq_dEWk;^$`)fJj0t!{DKgizR>jNT%hsWSQows{*(b@sCxMc~2(dvcGRq zM9I2=^y2q!M zR>kzX-UU^O!F>Z@doen}bEji-|qR=6g6jT3GH?bZ{w5 z$IR3zn>A*_CXow)h?S!s*f7&hB3i^!+0Q^~62ylt7wN+W0KCavOnpZ0iHqsf)<6=c zj>|DKR|blQZVF0{6}Rc$(M(2;jgiZ*i`Vziqltd(&>m4#ji61S zHjfrUQp*k09uoTP=Vqe?gT$Zj`|Bz86d7)z<01aTu2?BESC@4qWz_U)JHuX@@4hZV z8K&-FWn#8d_8BddZ#ZM}eUZUtQo+r0J5-4{4F&((;^Ech{b3-dOJnrK@Y*9}V}e-b8tN$2(4QkFS?c%EQ2K^QnSq3d0KJ_Bq4b^Q)*Gh*)}Y z8II*><#donzt3U_?JBG`*yz--m7`%Oc6bPdoO#fY{m(Hrm=Hj0EBsq95=y6lUo)Ee z??`QdP98c3ZC^U8{wQ>lgx8@t<4XI7u5NKlY9?#!mzE1bMA$GV$XC={e-sqiApk?L zVEJM?`Jl}p88f{8z~B^E?jfDiY21w`!iBb&3vDlEh-B4q0z%0{ z0F*+M^ZrhGls4P&E5cC^q7?U?9FQ^wUncmq2ri=MSl4hi^hK2%)?B-Y<$y7D6Kp*HiQ!?W_FA!Ey( zdT21ux?d0xiL-<;tH2-GH-N$dIar~C4{U@OTS56hGJ<11%}EiBF!;WXd(ZQnnfr82 z+=giroN_dry4`ezUZ4p9OTF@J*^cScwi)GjB-#KqrM3b&@{Xp9y%`jHuCr*yE|GyE zJ^eh5j71VfT;IyQ+dF!vJmqYZq+!ub*E%m?Ev1D-kS4@&nolghd*!4aX*5|+I4^0E zw;>UIGOlY{6^jI>>ua+7NgJ#;B_@yt4uWC-*W%w?2zF4ZDfEtM3L*$}vY6n|)1l-* z&>B{uR6L}Q4dKY-6B82a|L+DH+l6UPC{Ds9gF2eOCc=((d>c=+A4|H-7;tik;SnIw zgOY}6#X3n}lrfZKpkH<*V4?CCpb$md1a%z($ppDOEELAJ1Q;H)f*;<#a3tB-7>1Ri zQ!Q*4m@ElCvRq8yMsv#T<@x)NwF?u_1vlGwdIcE~_ut&=MIt>vbtX)3uS^S%OM77% zBxK@cZCau5=@-|H6|VE_xlM=0PcW(Fv85T8b>V6S%Yf2|4F%NJ)?_Ow5Xh1y3C-Tr zzuVE@UFg7ng7>>OGy;Qbh$oab5>h+){X)kfx4O2OYZmjB0^)Cuau-t%I7tNaXG)g_ z6X7lUMW=$f2^hvbZe;@ZE8^o$yCMe(fsfn_7`t+Q3#p%+hu~SAq?Mh@r7T&CDu_9^ z30#C^h#`Ukh3;3&Ex}!OQxEKBnDgrGxsaGQ7A?u}VJIm&mSY1T7+{F>O&OL{kA7GV zTC-79+N^?ymRX{likgEcyYjdEj4uPdy;%#)$TK?XvoSS@*4dA8<;jVdqiu>Aft9-i zA)A3-CBbrmKL%m53x!~uQdb1{j=kBstSUCQAF#rvOdQpfHe`edlhnq>EpQK2P6>*_ zf&ie{ca;%BH+}r!zNrq#(u4C{>dGL-GR{d9^06!*I@Vf>IQt4e7wW%6Eo({LW&?g8 zkM%R-cHl~o1n{Fi5a0muGZO?+XGq2~ID%N1i%G!0u!aQ!<_1$5JoVWjBh$nRKTmVE znE(_q^R~_rsPfSI0zO+KN>pl!HUvnZ`y%$N<4a*bO?_oZBtQmC6v3&3uiwi6O(7v4 zHW=QRAh2asScv$+nZ$mpWIoo^L_BpDWXsA)42ByiOA_vDY!0^?R4a!PYOoITan?sd z0t8fRE#TjSER?C9tf{dl60FK#%TCL8OWM_6pge7Qn77LK*tOhnQm{%}tDBae6 zUjS1}$c4Ac=&_>v?5ddnkTlFI5Xua@K18YmfY*=*IYjKX+-wPwyylqcl;(^emW@UV z^@YF^qeg+aRk9Hjp(eELMOn!0jf5;O!@=hG+$N+b3TD6>S8Ad9eC_q^A+gh{Z{vex zz-z^rL~^Ta5}c}+Sa5=mAz>8v!8VV$VZd}l$29$dg zznXT5&JH9hl3GBacMSWVScwi0yh!cawnU#hoV69QN|7a}mzD-x!)AMB1eS9Fa79Aw zn~5TLx}?Bs}d8|x+^LZ;35ye&En;}SZC|v*yUi* zc>08+XbEI#HqC=oYIS4?ZS2BQ0Y~^Dk%+Oy9`GkZOPWDI5e2*>wE&9UxrY$~N}D~VW#mF(tzwq%ivXbBI%LS-2w=#$=2S9Oa)Mb{ zu?TM50LFSU11z5c0}YvBXo#`FiG)OWD~)3CkOivf6nst(d`cBBnFlXXbT6(iN{cP_ zdfw2!;+zB0--w&5n3x-Mt~{23NihKoHr*jZpP&UlFBs2)_7*bW$Wa3ctOvbP%Y8r`OF^EYcgiD!C?fwRX^@U+wE{web9fdb`a(<3; zbK381-^OIEt=S)d1P4o7U%9nL#k9HNAxQvZc2NrvgaZMFqsYwbZ*m%t5QJpRECb=V zw-JDLZu&b=MJ<(%uKR-`tn%9`j1f8DH96cN!`^CrERLoO9|A@U_WmliFHqW6)hV9P@@$vCtF28XxY)I01w?;FrvPmX-zZk>Ne^lhYINSJ2#u-dXZcN_pre{!6 zNbqF?`kg))jb+Uua0WU^xxO$lOqy*~*BR|BS&<|4n3$1igf#y2hIlreSpdbhucswwG?X<1K8(l18JNh81hWvYC$XVH&C}Eje@P%T4 zUNaNf>n5k@$6Pfk*~MBLq^YaR@|A=?FQWbv37Ds;3YLDW};R2u%-a?f4`z2}b`;K*<0! zhAaseHu7T4A~5};uW21az5?wd${u?V7IZV4QmXNZ)F0Z8K~A|%)Rbqw>&0F{C3v-k z2!jyQ=VF$0176a~tY_2l;KqbuQiw<;C(zL`8bBAN0+FH5Dh~XCj6!!KpUOo!#Kb=P zMmoI)%BUM8-D4_}qJsk|s^x)@aLU?>03-rrve6%OCG#d8SMoU@T-pG-LiarT;z5`l zMDuEzoNkL)L2eTxwAo=R8>f%F@}J;w5Ya9axWg}?7FLtXlCpyYfkHBfu#^ExF&x;V z%Zobzq;fE6R39ly(CXXCnlT|Ojt`TT=|uW^ec>aZqw%rmSI+5aRc?iN3fRj53Sd6| zGu%_=0qOLs8U+EFhxXK@;PY&44d2#v?6!Fc+VnY}rN=??BsX(k+snztgbl+GX6m=t z9t!B&t10yHrt6B6lT{X87*ACJ?W?u-!UM)F0K^V5C$GbKxP26Ku=24bJJ4GC!kagk zs=O$KDh39O11GWJ@^qO3MmdBMkP&FcU{nl2kt1r~J!BT4S=%4FyXV%xDz~a3PyQ4o zc0|mR_-iY#IN^%aQX>f(QVg+n;*6;I2UD*+g0$m|0Mi_JFr@FXIYPVBL<_HzJDBPb z^onKl(|1dcO;Ine-*y*0-xB+whStRg!aH?4OdhY(b{PrPd(AfXyO_9-Yt`@ZIytU~ z8S&&v(#tF*e)m;l^AW-DL`34Ms`ALX+i?94V}YeM7ZBC2OE z3_2BR=e>^QrvXKmf|qiRV& zMDeaUJ4_wxP)F=={@x#uqlwCfvwsSBm8V&0Uz&nas;~?I2&!{7g>Kpfyqg+K`JIV& zkA)Q3;`Z@B0$@k!VNVyN^u_H+JC_0_@DO(8X&%XtZI{#nP zjc4{yRZ}1v^_I;Zzbm$b5oEiT``CXA)q2(6QYpCMBJiWQ|DKg^-*IoNJX84pehN{C zjROFbzzd|XrM@Vxw2W9hGU8)LJ=2*DoNEbfSj$t<=rb3)`*>aUU{B?5F9sf7LBz_M z%gbKsUQ{KJ5=!Gss(krC^%j6wLWR{tbqqcy3INrmmH@UIr3;}3d;?NKZ-1A2zVa{o zy?+wlu$mMA6Tu(?K&E-k4n=)z2;0IVShw~1TW&l3?siK(1a>C&WkeW<3}AB^B<_Jb zsi`h*`z=qcaExGGF@aO=M_!|CPN*@Z4vT!BSV&{zV2~)js6-pfVm`Uap7NDQAO)oo z6hXuAKR$`)y&mkzN!$aI4n|<@Wx0lz8cr^UXkmUXpf~~#=WND|Hcq3R~|z2OZcyURFmm~folLyg-4ob2|9>M4;OuSZLE@}OW;c01tHR! ztES>-fkJ?0on(c#YFId30{jC}fhfYvcozr*py1Y)bWsR^BsFRHoiT_auR~0pMVy{MbGwX6Ej-4K+s&8VY$T)8nI*4Ck2|o}#X)?6~8(0bGF7 XR8Yu*cZyFMr~emnML1B9JXgWUwB}H) diff --git a/worlds/pokemon_rb/basepatch_red.bsdiff4 b/worlds/pokemon_rb/basepatch_red.bsdiff4 index 488ae2727c4bbbcbc8cbfc7d6aa7ef1fabb7b747..cfcfab59b08ee28e567278d70bc64d549a755db0 100644 GIT binary patch literal 36528 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 Bh@Jod literal 29743 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 b31EbDb3?FVONK4{$Nnzlig2MJAY@hqPbju9 diff --git a/worlds/pokemon_rb/docs/en_Pokemon Red and Blue.md b/worlds/pokemon_rb/docs/en_Pokemon Red and Blue.md index fe2550d4a127..b857f234b0a2 100644 --- a/worlds/pokemon_rb/docs/en_Pokemon Red and Blue.md +++ b/worlds/pokemon_rb/docs/en_Pokemon Red and Blue.md @@ -32,6 +32,7 @@ fossil scientist. This may require reviving a number of fossils, depending on yo * Much of the dialogue throughout the game has been removed or shortened. * If the Old Man is blocking your way through Viridian City, you do not have Oak's Parcel in your inventory, and you've exhausted your money and Poké Balls, you can get a free Poké Ball from your mom. +* HM moves can be overwritten if you have the HM for it in your bag. ## What items and locations get shuffled? diff --git a/worlds/pokemon_rb/docs/setup_en.md b/worlds/pokemon_rb/docs/setup_en.md index ef0a5b3e84d5..5d83cb6598b0 100644 --- a/worlds/pokemon_rb/docs/setup_en.md +++ b/worlds/pokemon_rb/docs/setup_en.md @@ -25,8 +25,8 @@ Once Bizhawk has been installed, open Bizhawk and change the following settings: - Under Config > Customize > Advanced, make sure the box for AutoSaveRAM is checked, and click the 5s button. This reduces the possibility of losing save data in emulator crashes. -- Under Config > Customize, check the "Run in background" and "Accept background input" boxes. This will allow you to - continue playing in the background, even if another window is selected. +- Under Config > Customize, check the "Run in background" box. This will prevent disconnecting from the client while +BizHawk is running in the background. It is strongly recommended to associate GB rom extensions (\*.gb) to the Bizhawk we've just installed. To do so, we simply have to search any Gameboy rom we happened to own, right click and select "Open with...", unfold diff --git a/worlds/pokemon_rb/items.py b/worlds/pokemon_rb/items.py index 82ac6345d928..8afde91957b5 100644 --- a/worlds/pokemon_rb/items.py +++ b/worlds/pokemon_rb/items.py @@ -16,7 +16,7 @@ def __init__(self, id, classification, groups): "Bicycle": ItemData(6, ItemClassification.progression, ["Unique", "Key Items"]), # "Flippers": ItemData(7, ItemClassification.progression), #"Safari Ball": ItemData(8, ItemClassification.filler), - #"Pokedex": ItemData(9, ItemClassification.filler), + "Pokedex": ItemData(9, ItemClassification.progression, ["Unique", "Key Items"]), "Moon Stone": ItemData(10, ItemClassification.useful, ["Unique", "Evolution Stones"]), "Antidote": ItemData(11, ItemClassification.filler, ["Consumables"]), "Burn Heal": ItemData(12, ItemClassification.filler, ["Consumables"]), @@ -99,13 +99,17 @@ def __init__(self, id, classification, groups): "Mansion Key": ItemData(90, ItemClassification.progression, ["Unique", "Key Items"]), "Hideout Key": ItemData(91, ItemClassification.progression, ["Unique", "Key Items"]), "Safari Pass": ItemData(93, ItemClassification.progression, ["Unique", "Key Items"]), + "Poison Trap": ItemData(94, ItemClassification.trap, ["Traps"]), + "Paralyze Trap": ItemData(95, ItemClassification.trap, ["Traps"]), + "Ice Trap": ItemData(96, ItemClassification.trap, ["Traps"]), + "Fire Trap": ItemData(97, ItemClassification.trap, ["Traps"]), "HM01 Cut": ItemData(196, ItemClassification.progression, ["Unique", "HMs"]), "HM02 Fly": ItemData(197, ItemClassification.progression, ["Unique", "HMs"]), "HM03 Surf": ItemData(198, ItemClassification.progression, ["Unique", "HMs"]), "HM04 Strength": ItemData(199, ItemClassification.progression, ["Unique", "HMs"]), "HM05 Flash": ItemData(200, ItemClassification.progression, ["Unique", "HMs"]), "TM01 Mega Punch": ItemData(201, ItemClassification.useful, ["Unique", "TMs"]), - "TM02 Razor Wind": ItemData(202, ItemClassification.useful, ["Unique", "TMs"]), + "TM02 Razor Wind": ItemData(202, ItemClassification.filler, ["Unique", "TMs"]), "TM03 Swords Dance": ItemData(203, ItemClassification.useful, ["Unique", "TMs"]), "TM04 Whirlwind": ItemData(204, ItemClassification.filler, ["Unique", "TMs"]), "TM05 Mega Kick": ItemData(205, ItemClassification.useful, ["Unique", "TMs"]), @@ -139,12 +143,12 @@ def __init__(self, id, classification, groups): "TM33 Reflect": ItemData(233, ItemClassification.useful, ["Unique", "TMs"]), "TM34 Bide": ItemData(234, ItemClassification.filler, ["Unique", "TMs"]), "TM35 Metronome": ItemData(235, ItemClassification.useful, ["Unique", "TMs"]), - "TM36 Self Destruct": ItemData(236, ItemClassification.useful, ["Unique", "TMs"]), + "TM36 Self-Destruct": ItemData(236, ItemClassification.useful, ["Unique", "TMs"]), "TM37 Egg Bomb": ItemData(237, ItemClassification.useful, ["Unique", "TMs"]), "TM38 Fire Blast": ItemData(238, ItemClassification.useful, ["Unique", "TMs"]), "TM39 Swift": ItemData(239, ItemClassification.useful, ["Unique", "TMs"]), "TM40 Skull Bash": ItemData(240, ItemClassification.filler, ["Unique", "TMs"]), - "TM41 Soft Boiled": ItemData(241, ItemClassification.useful, ["Unique", "TMs"]), + "TM41 Soft-Boiled": ItemData(241, ItemClassification.useful, ["Unique", "TMs"]), "TM42 Dream Eater": ItemData(242, ItemClassification.useful, ["Unique", "TMs"]), "TM43 Sky Attack": ItemData(243, ItemClassification.filler, ["Unique", "TMs"]), "TM44 Rest": ItemData(244, ItemClassification.useful, ["Unique", "TMs"]), diff --git a/worlds/pokemon_rb/locations.py b/worlds/pokemon_rb/locations.py index 3ba7eb773003..3e0148a80316 100644 --- a/worlds/pokemon_rb/locations.py +++ b/worlds/pokemon_rb/locations.py @@ -3,8 +3,34 @@ from .rom_addresses import rom_addresses loc_id_start = 172000000 + +def trainersanity(multiworld, player): + return multiworld.trainersanity[player] + + +def hidden_items(multiworld, player): + return multiworld.randomize_hidden_items[player].value > 0 + + +def tea(multiworld, player): + return multiworld.tea[player] + + +def extra_key_items(multiworld, player): + return multiworld.extra_key_items[player] + + +def pokedex(multiworld, player): + return multiworld.randomize_pokedex[player].value > 0 + + +def always_on(multiworld, player): + return True + + class LocationData: - def __init__(self, region, name, original_item, rom_address=None, ram_address=None, event=False, type="Item"): + + def __init__(self, region, name, original_item, rom_address=None, ram_address=None, event=False, type="Item", inclusion=always_on): self.region = region if "Route" in region: region = " ".join(region.split()[:2]) @@ -14,6 +40,7 @@ def __init__(self, region, name, original_item, rom_address=None, ram_address=No self.ram_address = ram_address self.event = event self.type = type + self.inclusion = inclusion class EventFlag: def __init__(self, flag): @@ -42,6 +69,7 @@ def __init__(self, flag): self.bit = flag self.flag = flag + location_data = [ LocationData("Vermilion City", "Fishing Guru", "Old Rod", rom_addresses["Rod_Vermilion_City_Fishing_Guru"], Rod(3)), @@ -49,7 +77,7 @@ def __init__(self, flag): LocationData("Route 12 South", "Fishing Guru's Brother", "Super Rod", rom_addresses["Rod_Route12_Fishing_Brother"], Rod(5)), LocationData("Pallet Town", "Player's PC", "Potion", rom_addresses['PC_Item'], EventFlag(1),), - LocationData("Celadon City", "Mansion Lady", "Tea", rom_addresses["Event_Mansion_Lady"], EventFlag(2)), + LocationData("Celadon City", "Mansion Lady", "Tea", rom_addresses["Event_Mansion_Lady"], EventFlag(2), inclusion=tea), LocationData("Pallet Town", "Rival's Sister", "Town Map", rom_addresses["Event_Rivals_Sister"], EventFlag(24)), LocationData("Pallet Town", "Oak's Post-Route-22-Rival Gift", "Poke Ball", rom_addresses["Event_Oaks_Gift"], EventFlag(36)), LocationData("Route 1", "Free Sample Man", "Potion", rom_addresses["Event_Free_Sample"], EventFlag(960)), @@ -77,7 +105,7 @@ def __init__(self, flag): LocationData("S.S. Anne 2F", "Captain", "HM01 Cut", rom_addresses["Event_SS_Anne_Captain"], EventFlag(1504)), LocationData("Route 11 East", "Oak's Aide", "Item Finder", rom_addresses["Event_Rt11_Oaks_Aide"], EventFlag(1151)), - LocationData("Celadon City", "Stranded Man", "TM41 Soft Boiled", rom_addresses["Event_Stranded_Man"], + LocationData("Celadon City", "Stranded Man", "TM41 Soft-Boiled", rom_addresses["Event_Stranded_Man"], EventFlag(384)), LocationData("Celadon City", "Thirsty Girl Gets Water", "TM13 Ice Beam", rom_addresses["Event_Thirsty_Girl_Water"], EventFlag(396)), @@ -91,7 +119,7 @@ def __init__(self, flag): LocationData("Celadon Gym", "Erika 2", "TM21 Mega Drain", rom_addresses["Event_Celadon_Gym"], EventFlag(424)), LocationData("Silph Co 11F", "Silph Co President", "Master Ball", rom_addresses["Event_Silph_Co_President"], EventFlag(1933)), - LocationData("Silph Co 2F", "Woman", "TM36 Self Destruct", rom_addresses["Event_Scared_Woman"], + LocationData("Silph Co 2F", "Woman", "TM36 Self-Destruct", rom_addresses["Event_Scared_Woman"], EventFlag(1791)), LocationData("Route 16 North", "House Woman", "HM02 Fly", rom_addresses["Event_Rt16_House_Woman"], EventFlag(1230)), LocationData("Route 15", "Oak's Aide", "Exp. All", rom_addresses["Event_Rt_15_Oaks_Aide"], EventFlag(1200)), @@ -299,13 +327,13 @@ def __init__(self, flag): LocationData("Victory Road 1F", "Left Item", "Rare Candy", rom_addresses["Missable_Victory_Road_1F_Item_2"], Missable(213)), LocationData("Rock Tunnel B1F", "Southwest Item", "Hideout Key", rom_addresses["Missable_Rock_Tunnel_B1F_Item_1"], - Missable(231)), + Missable(231), inclusion=extra_key_items), LocationData("Rock Tunnel B1F", "West Item", "Mansion Key", rom_addresses["Missable_Rock_Tunnel_B1F_Item_2"], - Missable(232)), + Missable(232), inclusion=extra_key_items), LocationData("Rock Tunnel B1F", "Northwest Item", "Plant Key", rom_addresses["Missable_Rock_Tunnel_B1F_Item_3"], - Missable(233)), + Missable(233), inclusion=extra_key_items), LocationData("Rock Tunnel B1F", "North Item", "Safari Pass", rom_addresses["Missable_Rock_Tunnel_B1F_Item_4"], - Missable(234)), + Missable(234), inclusion=extra_key_items), LocationData("Pewter Gym", "Brock 1", "Boulder Badge", rom_addresses['Badge_Pewter_Gym'], EventFlag(0x8A0)), LocationData("Cerulean Gym", "Misty 1", "Cascade Badge", rom_addresses['Badge_Cerulean_Gym'], EventFlag(0x8A1)), @@ -316,59 +344,372 @@ def __init__(self, flag): LocationData("Cinnabar Gym", "Blaine 1", "Volcano Badge", rom_addresses['Badge_Cinnabar_Gym'], EventFlag(0x8A6)), LocationData("Viridian Gym", "Giovanni 1", "Earth Badge", rom_addresses['Badge_Viridian_Gym'], EventFlag(0x8A7)), - LocationData("Viridian Forest", "Hidden Item Northwest by Trainer", "Potion", rom_addresses['Hidden_Item_Viridian_Forest_1'], Hidden(0)), - LocationData("Viridian Forest", "Hidden Item Entrance Tree", "Antidote", rom_addresses['Hidden_Item_Viridian_Forest_2'], Hidden(1)), - LocationData("Mt Moon B2F", "Hidden Item Dead End Before Fossils", "Moon Stone", rom_addresses['Hidden_Item_MtMoonB2F_1'], Hidden(2)), - LocationData("Route 25", "Hidden Item Fence Outside Bill's House", "Ether", rom_addresses['Hidden_Item_Route_25_1'], Hidden(3)), - LocationData("Route 9", "Hidden Item Bush By Grass", "Ether", rom_addresses['Hidden_Item_Route_9'], Hidden(4)), - LocationData("S.S. Anne 1F", "Hidden Item Kitchen Trash", "Great Ball", rom_addresses['Hidden_Item_SS_Anne_Kitchen'], Hidden(5)), - LocationData("S.S. Anne B1F", "Hidden Item Under Pillow", "Hyper Potion", rom_addresses['Hidden_Item_SS_Anne_B1F'], Hidden(6)), - LocationData("Route 10 North", "Hidden Item Behind Rock Tunnel Entrance Cuttable Tree", "Super Potion", rom_addresses['Hidden_Item_Route_10_1'], Hidden(7)), - LocationData("Route 10 South", "Hidden Item Bush", "Max Ether", rom_addresses['Hidden_Item_Route_10_2'], Hidden(8)), - LocationData("Rocket Hideout B1F", "Hidden Item Pot Plant", "PP Up", rom_addresses['Hidden_Item_Rocket_Hideout_B1F'], Hidden(9)), - LocationData("Rocket Hideout B3F", "Hidden Item Near East Item", "Nugget", rom_addresses['Hidden_Item_Rocket_Hideout_B3F'], Hidden(10)), - LocationData("Rocket Hideout B4F", "Hidden Item Behind Giovanni", "Super Potion", rom_addresses['Hidden_Item_Rocket_Hideout_B4F'], Hidden(11)), - LocationData("Pokemon Tower 5F", "Hidden Item Near West Staircase", "Elixir", rom_addresses['Hidden_Item_Pokemon_Tower_5F'], Hidden(12)), - LocationData("Route 13", "Hidden Item Dead End Bush", "PP Up", rom_addresses['Hidden_Item_Route_13_1'], Hidden(13)), - LocationData("Route 13", "Hidden Item Dead End By Water Corner", "Calcium", rom_addresses['Hidden_Item_Route_13_2'], Hidden(14)), - LocationData("Pokemon Mansion B1F", "Hidden Item Secret Key Room Corner", "Rare Candy", rom_addresses['Hidden_Item_Pokemon_Mansion_B1F'], Hidden(15)), - LocationData("Safari Zone West", "Hidden Item Secret House Statue", "Revive", rom_addresses['Hidden_Item_Safari_Zone_West'], Hidden(17)), - LocationData("Silph Co 5F", "Hidden Item Pot Plant", "Elixir", rom_addresses['Hidden_Item_Silph_Co_5F'], Hidden(18)), - LocationData("Silph Co 9F", "Hidden Item Nurse Bed (Card Key)", "Max Potion", rom_addresses['Hidden_Item_Silph_Co_9F'], Hidden(19)), - LocationData("Copycat's House", "Hidden Item Desk", "Nugget", rom_addresses['Hidden_Item_Copycats_House'], Hidden(20)), - LocationData("Cerulean Cave 1F", "Hidden Item Center Rocks", "Rare Candy", rom_addresses['Hidden_Item_Cerulean_Cave_1F'], Hidden(21)), - LocationData("Cerulean Cave B1F", "Hidden Item Northeast Rocks", "Ultra Ball", rom_addresses['Hidden_Item_Cerulean_Cave_B1F'], Hidden(22)), - LocationData("Power Plant", "Hidden Item Central Dead End", "Max Elixir", rom_addresses['Hidden_Item_Power_Plant_1'], Hidden(23)), - LocationData("Power Plant", "Hidden Item Before Zapdos", "PP Up", rom_addresses['Hidden_Item_Power_Plant_2'], Hidden(24)), - LocationData("Seafoam Islands B2F", "Hidden Item Rock", "Nugget", rom_addresses['Hidden_Item_Seafoam_Islands_B2F'], Hidden(25)), - LocationData("Seafoam Islands B4F", "Hidden Item Corner Island", "Ultra Ball", rom_addresses['Hidden_Item_Seafoam_Islands_B4F'], Hidden(26)), - LocationData("Pokemon Mansion 1F", "Hidden Item Block Near Entrance Carpet", "Moon Stone", rom_addresses['Hidden_Item_Pokemon_Mansion_1F'], Hidden(27)), - LocationData("Pokemon Mansion 3F", "Hidden Item Behind Burglar", "Max Revive", rom_addresses['Hidden_Item_Pokemon_Mansion_3F'], Hidden(28)), - LocationData("Route 23", "Hidden Item Rocks Before Final Guard", "Full Restore", rom_addresses['Hidden_Item_Route_23_1'], Hidden(29)), - LocationData("Route 23", "Hidden Item East Bush After Water", "Ultra Ball", rom_addresses['Hidden_Item_Route_23_2'], Hidden(30)), - LocationData("Route 23", "Hidden Item On Island", "Max Ether", rom_addresses['Hidden_Item_Route_23_3'], Hidden(31)), - LocationData("Victory Road 2F", "Hidden Item Rock Before Moltres", "Ultra Ball", rom_addresses['Hidden_Item_Victory_Road_2F_1'], Hidden(32)), - LocationData("Victory Road 2F", "Hidden Item Rock In Final Room", "Full Restore", rom_addresses['Hidden_Item_Victory_Road_2F_2'], Hidden(33)), + LocationData("Viridian Forest", "Hidden Item Northwest by Trainer", "Potion", rom_addresses['Hidden_Item_Viridian_Forest_1'], Hidden(0), inclusion=hidden_items), + LocationData("Viridian Forest", "Hidden Item Entrance Tree", "Antidote", rom_addresses['Hidden_Item_Viridian_Forest_2'], Hidden(1), inclusion=hidden_items), + LocationData("Mt Moon B2F", "Hidden Item Dead End Before Fossils", "Moon Stone", rom_addresses['Hidden_Item_MtMoonB2F_1'], Hidden(2), inclusion=hidden_items), + LocationData("Route 25", "Hidden Item Fence Outside Bill's House", "Ether", rom_addresses['Hidden_Item_Route_25_1'], Hidden(3), inclusion=hidden_items), + LocationData("Route 9", "Hidden Item Bush By Grass", "Ether", rom_addresses['Hidden_Item_Route_9'], Hidden(4), inclusion=hidden_items), + LocationData("S.S. Anne 1F", "Hidden Item Kitchen Trash", "Great Ball", rom_addresses['Hidden_Item_SS_Anne_Kitchen'], Hidden(5), inclusion=hidden_items), + LocationData("S.S. Anne B1F", "Hidden Item Under Pillow", "Hyper Potion", rom_addresses['Hidden_Item_SS_Anne_B1F'], Hidden(6), inclusion=hidden_items), + LocationData("Route 10 North", "Hidden Item Behind Rock Tunnel Entrance Cuttable Tree", "Super Potion", rom_addresses['Hidden_Item_Route_10_1'], Hidden(7), inclusion=hidden_items), + LocationData("Route 10 South", "Hidden Item Bush", "Max Ether", rom_addresses['Hidden_Item_Route_10_2'], Hidden(8), inclusion=hidden_items), + LocationData("Rocket Hideout B1F", "Hidden Item Pot Plant", "PP Up", rom_addresses['Hidden_Item_Rocket_Hideout_B1F'], Hidden(9), inclusion=hidden_items), + LocationData("Rocket Hideout B3F", "Hidden Item Near East Item", "Nugget", rom_addresses['Hidden_Item_Rocket_Hideout_B3F'], Hidden(10), inclusion=hidden_items), + LocationData("Rocket Hideout B4F", "Hidden Item Behind Giovanni", "Super Potion", rom_addresses['Hidden_Item_Rocket_Hideout_B4F'], Hidden(11), inclusion=hidden_items), + LocationData("Pokemon Tower 5F", "Hidden Item Near West Staircase", "Elixir", rom_addresses['Hidden_Item_Pokemon_Tower_5F'], Hidden(12), inclusion=hidden_items), + LocationData("Route 13", "Hidden Item Dead End Bush", "PP Up", rom_addresses['Hidden_Item_Route_13_1'], Hidden(13), inclusion=hidden_items), + LocationData("Route 13", "Hidden Item Dead End By Water Corner", "Calcium", rom_addresses['Hidden_Item_Route_13_2'], Hidden(14), inclusion=hidden_items), + LocationData("Pokemon Mansion B1F", "Hidden Item Secret Key Room Corner", "Rare Candy", rom_addresses['Hidden_Item_Pokemon_Mansion_B1F'], Hidden(15), inclusion=hidden_items), + LocationData("Safari Zone West", "Hidden Item Secret House Statue", "Revive", rom_addresses['Hidden_Item_Safari_Zone_West'], Hidden(17), inclusion=hidden_items), + LocationData("Silph Co 5F", "Hidden Item Pot Plant", "Elixir", rom_addresses['Hidden_Item_Silph_Co_5F'], Hidden(18), inclusion=hidden_items), + LocationData("Silph Co 9F", "Hidden Item Nurse Bed (Card Key)", "Max Potion", rom_addresses['Hidden_Item_Silph_Co_9F'], Hidden(19), inclusion=hidden_items), + LocationData("Copycat's House", "Hidden Item Desk", "Nugget", rom_addresses['Hidden_Item_Copycats_House'], Hidden(20), inclusion=hidden_items), + LocationData("Cerulean Cave 1F", "Hidden Item Center Rocks", "Rare Candy", rom_addresses['Hidden_Item_Cerulean_Cave_1F'], Hidden(21), inclusion=hidden_items), + LocationData("Cerulean Cave B1F", "Hidden Item Northeast Rocks", "Ultra Ball", rom_addresses['Hidden_Item_Cerulean_Cave_B1F'], Hidden(22), inclusion=hidden_items), + LocationData("Power Plant", "Hidden Item Central Dead End", "Max Elixir", rom_addresses['Hidden_Item_Power_Plant_1'], Hidden(23), inclusion=hidden_items), + LocationData("Power Plant", "Hidden Item Before Zapdos", "PP Up", rom_addresses['Hidden_Item_Power_Plant_2'], Hidden(24), inclusion=hidden_items), + LocationData("Seafoam Islands B2F", "Hidden Item Rock", "Nugget", rom_addresses['Hidden_Item_Seafoam_Islands_B2F'], Hidden(25), inclusion=hidden_items), + LocationData("Seafoam Islands B4F", "Hidden Item Corner Island", "Ultra Ball", rom_addresses['Hidden_Item_Seafoam_Islands_B4F'], Hidden(26), inclusion=hidden_items), + LocationData("Pokemon Mansion 1F", "Hidden Item Block Near Entrance Carpet", "Moon Stone", rom_addresses['Hidden_Item_Pokemon_Mansion_1F'], Hidden(27), inclusion=hidden_items), + LocationData("Pokemon Mansion 3F", "Hidden Item Behind Burglar", "Max Revive", rom_addresses['Hidden_Item_Pokemon_Mansion_3F'], Hidden(28), inclusion=hidden_items), + LocationData("Route 23", "Hidden Item Rocks Before Final Guard", "Full Restore", rom_addresses['Hidden_Item_Route_23_1'], Hidden(29), inclusion=hidden_items), + LocationData("Route 23", "Hidden Item East Bush After Water", "Ultra Ball", rom_addresses['Hidden_Item_Route_23_2'], Hidden(30), inclusion=hidden_items), + LocationData("Route 23", "Hidden Item On Island", "Max Ether", rom_addresses['Hidden_Item_Route_23_3'], Hidden(31), inclusion=hidden_items), + LocationData("Victory Road 2F", "Hidden Item Rock Before Moltres", "Ultra Ball", rom_addresses['Hidden_Item_Victory_Road_2F_1'], Hidden(32), inclusion=hidden_items), + LocationData("Victory Road 2F", "Hidden Item Rock In Final Room", "Full Restore", rom_addresses['Hidden_Item_Victory_Road_2F_2'], Hidden(33), inclusion=hidden_items), + + LocationData("Viridian City", "Hidden Item Cuttable Tree", "Potion", rom_addresses['Hidden_Item_Viridian_City'], Hidden(35), inclusion=hidden_items), + LocationData("Route 11", "Hidden Item Isolated Bush Near Gate", "Potion", rom_addresses['Hidden_Item_Route_11'], Hidden(36), inclusion=hidden_items), + LocationData("Route 12 West", "Hidden Item Bush Near Gate", "Hyper Potion", rom_addresses['Hidden_Item_Route_12'], Hidden(37), inclusion=hidden_items), + LocationData("Route 17", "Hidden Item In Grass", "Rare Candy", rom_addresses['Hidden_Item_Route_17_1'], Hidden(38), inclusion=hidden_items), + LocationData("Route 17", "Hidden Item Near Northernmost Sign", "Full Restore", rom_addresses['Hidden_Item_Route_17_2'], Hidden(39), inclusion=hidden_items), + LocationData("Route 17", "Hidden Item East Center", "PP Up", rom_addresses['Hidden_Item_Route_17_3'], Hidden(40), inclusion=hidden_items), + LocationData("Route 17", "Hidden Item West Center", "Max Revive", rom_addresses['Hidden_Item_Route_17_4'], Hidden(41), inclusion=hidden_items), + LocationData("Route 17", "Hidden Item Before Final Bridge", "Max Elixir", rom_addresses['Hidden_Item_Route_17_5'], Hidden(42), inclusion=hidden_items), + LocationData("Underground Tunnel North-South", "Hidden Item Near Northern Stairs", "Full Restore", rom_addresses['Hidden_Item_Underground_Path_NS_1'], Hidden(43), inclusion=hidden_items), + LocationData("Underground Tunnel North-South", "Hidden Item Near Southern Stairs", "X Special", rom_addresses['Hidden_Item_Underground_Path_NS_2'], Hidden(44), inclusion=hidden_items), + LocationData("Underground Tunnel West-East", "Hidden Item West", "Nugget", rom_addresses['Hidden_Item_Underground_Path_WE_1'], Hidden(45), inclusion=hidden_items), + LocationData("Underground Tunnel West-East", "Hidden Item East", "Elixir", rom_addresses['Hidden_Item_Underground_Path_WE_2'], Hidden(46), inclusion=hidden_items), + LocationData("Celadon City", "Hidden Item Dead End Near Cuttable Tree", "PP Up", rom_addresses['Hidden_Item_Celadon_City'], Hidden(47), inclusion=hidden_items), + LocationData("Route 25", "Hidden Item Northeast Of Grass", "Elixir", rom_addresses['Hidden_Item_Route_25_2'], Hidden(48), inclusion=hidden_items), + LocationData("Mt Moon B2F", "Hidden Item Lone Rock", "Ether", rom_addresses['Hidden_Item_MtMoonB2F_2'], Hidden(49), inclusion=hidden_items), + LocationData("Seafoam Islands B3F", "Hidden Item Rock", "Max Elixir", rom_addresses['Hidden_Item_Seafoam_Islands_B3F'], Hidden(50), inclusion=hidden_items), + LocationData("Vermilion City", "Hidden Item In Water Near Fan Club", "Max Ether", rom_addresses['Hidden_Item_Vermilion_City'], Hidden(51), inclusion=hidden_items), + LocationData("Cerulean City", "Hidden Item Gym Badge Guy's Backyard", "Rare Candy", rom_addresses['Hidden_Item_Cerulean_City'], Hidden(52), inclusion=hidden_items), + LocationData("Route 4", "Hidden Item Plateau East Of Mt Moon", "Great Ball", rom_addresses['Hidden_Item_Route_4'], Hidden(53), inclusion=hidden_items), - LocationData("Viridian City", "Hidden Item Cuttable Tree", "Potion", rom_addresses['Hidden_Item_Viridian_City'], Hidden(35)), - LocationData("Route 11", "Hidden Item Isolated Bush Near Gate", "Potion", rom_addresses['Hidden_Item_Route_11'], Hidden(36)), - LocationData("Route 12 West", "Hidden Item Bush Near Gate", "Hyper Potion", rom_addresses['Hidden_Item_Route_12'], Hidden(37)), - LocationData("Route 17", "Hidden Item In Grass", "Rare Candy", rom_addresses['Hidden_Item_Route_17_1'], Hidden(38)), - LocationData("Route 17", "Hidden Item Near Northernmost Sign", "Full Restore", rom_addresses['Hidden_Item_Route_17_2'], Hidden(39)), - LocationData("Route 17", "Hidden Item East Center", "PP Up", rom_addresses['Hidden_Item_Route_17_3'], Hidden(40)), - LocationData("Route 17", "Hidden Item West Center", "Max Revive", rom_addresses['Hidden_Item_Route_17_4'], Hidden(41)), - LocationData("Route 17", "Hidden Item Before Final Bridge", "Max Elixir", rom_addresses['Hidden_Item_Route_17_5'], Hidden(42)), - LocationData("Underground Tunnel North-South", "Hidden Item Near Northern Stairs", "Full Restore", rom_addresses['Hidden_Item_Underground_Path_NS_1'], Hidden(43)), - LocationData("Underground Tunnel North-South", "Hidden Item Near Southern Stairs", "X Special", rom_addresses['Hidden_Item_Underground_Path_NS_2'], Hidden(44)), - LocationData("Underground Tunnel West-East", "Hidden Item West", "Nugget", rom_addresses['Hidden_Item_Underground_Path_WE_1'], Hidden(45)), - LocationData("Underground Tunnel West-East", "Hidden Item East", "Elixir", rom_addresses['Hidden_Item_Underground_Path_WE_2'], Hidden(46)), - LocationData("Celadon City", "Hidden Item Dead End Near Cuttable Tree", "PP Up", rom_addresses['Hidden_Item_Celadon_City'], Hidden(47)), - LocationData("Route 25", "Hidden Item Northeast Of Grass", "Elixir", rom_addresses['Hidden_Item_Route_25_2'], Hidden(48)), - LocationData("Mt Moon B2F", "Hidden Item Lone Rock", "Ether", rom_addresses['Hidden_Item_MtMoonB2F_2'], Hidden(49)), - LocationData("Seafoam Islands B3F", "Hidden Item Rock", "Max Elixir", rom_addresses['Hidden_Item_Seafoam_Islands_B3F'], Hidden(50)), - LocationData("Vermilion City", "Hidden Item In Water Near Fan Club", "Max Ether", rom_addresses['Hidden_Item_Vermilion_City'], Hidden(51)), - LocationData("Cerulean City", "Hidden Item Gym Badge Guy's Backyard", "Rare Candy", rom_addresses['Hidden_Item_Cerulean_City'], Hidden(52)), - LocationData("Route 4", "Hidden Item Plateau East Of Mt Moon", "Great Ball", rom_addresses['Hidden_Item_Route_4'], Hidden(53)), + LocationData("Pallet Town", "Oak's Parcel Reward", "Pokedex", rom_addresses["Event_Pokedex"], EventFlag(0x38), inclusion=pokedex), + + LocationData("Pokemon Mansion 1F", "Scientist", None, rom_addresses["Trainersanity_EVENT_BEAT_MANSION_1_TRAINER_0_ITEM"], EventFlag(376), inclusion=trainersanity), + LocationData("Pokemon Mansion 2F", "Burglar", None, rom_addresses["Trainersanity_EVENT_BEAT_MANSION_2_TRAINER_0_ITEM"], EventFlag(43), inclusion=trainersanity), + LocationData("Pokemon Mansion 3F", "Scientist", None, rom_addresses["Trainersanity_EVENT_BEAT_MANSION_3_TRAINER_1_ITEM"], EventFlag(31), inclusion=trainersanity), + LocationData("Pokemon Mansion 3F", "Burglar", None, rom_addresses["Trainersanity_EVENT_BEAT_MANSION_3_TRAINER_0_ITEM"], EventFlag(42), inclusion=trainersanity), + LocationData("Pokemon Mansion B1F", "Scientist", None, rom_addresses["Trainersanity_EVENT_BEAT_MANSION_4_TRAINER_1_ITEM"], EventFlag(29), inclusion=trainersanity), + LocationData("Pokemon Mansion B1F", "Burglar", None, rom_addresses["Trainersanity_EVENT_BEAT_MANSION_4_TRAINER_0_ITEM"], EventFlag(30), inclusion=trainersanity), + LocationData("Silph Co 11F", "Rocket 1", None, rom_addresses["Trainersanity_EVENT_BEAT_SILPH_CO_11F_TRAINER_1_ITEM"], EventFlag(45), inclusion=trainersanity), + LocationData("Silph Co 11F", "Rocket 2 (Card Key)", None, rom_addresses["Trainersanity_EVENT_BEAT_SILPH_CO_11F_TRAINER_0_ITEM"], EventFlag(46), inclusion=trainersanity), + LocationData("Silph Co 10F", "Scientist", None, rom_addresses["Trainersanity_EVENT_BEAT_SILPH_CO_10F_TRAINER_1_ITEM"], EventFlag(47), inclusion=trainersanity), + LocationData("Silph Co 10F", "Rocket", None, rom_addresses["Trainersanity_EVENT_BEAT_SILPH_CO_10F_TRAINER_0_ITEM"], EventFlag(48), inclusion=trainersanity), + LocationData("Silph Co 9F", "Rocket 1", None, rom_addresses["Trainersanity_EVENT_BEAT_SILPH_CO_9F_TRAINER_2_ITEM"], EventFlag(49), inclusion=trainersanity), + LocationData("Silph Co 9F", "Scientist", None, rom_addresses["Trainersanity_EVENT_BEAT_SILPH_CO_9F_TRAINER_1_ITEM"], EventFlag(50), inclusion=trainersanity), + LocationData("Silph Co 9F", "Rocket 2 (Card Key)", None, rom_addresses["Trainersanity_EVENT_BEAT_SILPH_CO_9F_TRAINER_0_ITEM"], EventFlag(51), inclusion=trainersanity), + LocationData("Silph Co 8F", "Rocket 1", None, rom_addresses["Trainersanity_EVENT_BEAT_SILPH_CO_8F_TRAINER_0_ITEM"], EventFlag(54), inclusion=trainersanity), + LocationData("Silph Co 8F", "Rocket 2", None, rom_addresses["Trainersanity_EVENT_BEAT_SILPH_CO_8F_TRAINER_2_ITEM"], EventFlag(52), inclusion=trainersanity), + LocationData("Silph Co 8F", "Scientist", None, rom_addresses["Trainersanity_EVENT_BEAT_SILPH_CO_8F_TRAINER_1_ITEM"], EventFlag(53), inclusion=trainersanity), + LocationData("Silph Co 7F", "Rocket 1", None, rom_addresses["Trainersanity_EVENT_BEAT_SILPH_CO_7F_TRAINER_2_ITEM"], EventFlag(58), inclusion=trainersanity), + LocationData("Silph Co 7F", "Rocket 2", None, rom_addresses["Trainersanity_EVENT_BEAT_SILPH_CO_7F_TRAINER_0_ITEM"], EventFlag(60), inclusion=trainersanity), + LocationData("Silph Co 7F", "Scientist", None, rom_addresses["Trainersanity_EVENT_BEAT_SILPH_CO_7F_TRAINER_1_ITEM"], EventFlag(59), inclusion=trainersanity), + LocationData("Silph Co 7F", "Rocket 3", None, rom_addresses["Trainersanity_EVENT_BEAT_SILPH_CO_7F_TRAINER_3_ITEM"], EventFlag(55), inclusion=trainersanity), + LocationData("Silph Co 6F", "Rocket 1", None, rom_addresses["Trainersanity_EVENT_BEAT_SILPH_CO_6F_TRAINER_0_ITEM"], EventFlag(64), inclusion=trainersanity), + LocationData("Silph Co 6F", "Scientist", None, rom_addresses["Trainersanity_EVENT_BEAT_SILPH_CO_6F_TRAINER_1_ITEM"], EventFlag(63), inclusion=trainersanity), + LocationData("Silph Co 6F", "Rocket 2", None, rom_addresses["Trainersanity_EVENT_BEAT_SILPH_CO_6F_TRAINER_2_ITEM"], EventFlag(62), inclusion=trainersanity), + LocationData("Silph Co 5F", "Rocket 1", None, rom_addresses["Trainersanity_EVENT_BEAT_SILPH_CO_5F_TRAINER_3_ITEM"], EventFlag(65), inclusion=trainersanity), + LocationData("Silph Co 5F", "Juggler", None, rom_addresses["Trainersanity_EVENT_BEAT_SILPH_CO_5F_TRAINER_2_ITEM"], EventFlag(66), inclusion=trainersanity), + LocationData("Silph Co 5F", "Scientist", None, rom_addresses["Trainersanity_EVENT_BEAT_SILPH_CO_5F_TRAINER_1_ITEM"], EventFlag(67), inclusion=trainersanity), + LocationData("Silph Co 5F", "Rocket 2", None, rom_addresses["Trainersanity_EVENT_BEAT_SILPH_CO_5F_TRAINER_0_ITEM"], EventFlag(68), inclusion=trainersanity), + LocationData("Silph Co 4F", "Rocket 1", None, rom_addresses["Trainersanity_EVENT_BEAT_SILPH_CO_4F_TRAINER_2_ITEM"], EventFlag(69), inclusion=trainersanity), + LocationData("Silph Co 4F", "Rocket 2", None, rom_addresses["Trainersanity_EVENT_BEAT_SILPH_CO_4F_TRAINER_0_ITEM"], EventFlag(71), inclusion=trainersanity), + LocationData("Silph Co 4F", "Scientist", None, rom_addresses["Trainersanity_EVENT_BEAT_SILPH_CO_4F_TRAINER_1_ITEM"], EventFlag(70), inclusion=trainersanity), + LocationData("Silph Co 3F", "Scientist (Card Key)", None, rom_addresses["Trainersanity_EVENT_BEAT_SILPH_CO_3F_TRAINER_1_ITEM"], EventFlag(72), inclusion=trainersanity), + LocationData("Silph Co 3F", "Rocket", None, rom_addresses["Trainersanity_EVENT_BEAT_SILPH_CO_3F_TRAINER_0_ITEM"], EventFlag(73), inclusion=trainersanity), + LocationData("Silph Co 2F", "Rocket 1", None, rom_addresses["Trainersanity_EVENT_BEAT_SILPH_CO_2F_TRAINER_3_ITEM"], EventFlag(74), inclusion=trainersanity), + LocationData("Silph Co 2F", "Rocket 2", None, rom_addresses["Trainersanity_EVENT_BEAT_SILPH_CO_2F_TRAINER_2_ITEM"], EventFlag(75), inclusion=trainersanity), + LocationData("Silph Co 2F", "Scientist 1", None, rom_addresses["Trainersanity_EVENT_BEAT_SILPH_CO_2F_TRAINER_1_ITEM"], EventFlag(76), inclusion=trainersanity), + LocationData("Silph Co 2F", "Scientist 2", None, rom_addresses["Trainersanity_EVENT_BEAT_SILPH_CO_2F_TRAINER_0_ITEM"], EventFlag(77), inclusion=trainersanity), + LocationData("Rocket Hideout B1F", "Rocket 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROCKET_HIDEOUT_1_TRAINER_0_ITEM"], EventFlag(99), inclusion=trainersanity), + LocationData("Rocket Hideout B1F", "Rocket 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROCKET_HIDEOUT_1_TRAINER_1_ITEM"], EventFlag(98), inclusion=trainersanity), + LocationData("Rocket Hideout B1F", "Rocket 3", None, rom_addresses["Trainersanity_EVENT_BEAT_ROCKET_HIDEOUT_1_TRAINER_3_ITEM"], EventFlag(96), inclusion=trainersanity), + LocationData("Rocket Hideout B1F", "Rocket 4", None, rom_addresses["Trainersanity_EVENT_BEAT_ROCKET_HIDEOUT_1_TRAINER_2_ITEM"], EventFlag(97), inclusion=trainersanity), + LocationData("Rocket Hideout B1F", "Rocket 5 (Lift Key)", None, rom_addresses["Trainersanity_EVENT_BEAT_ROCKET_HIDEOUT_1_TRAINER_4_ITEM"], EventFlag(95), inclusion=trainersanity), + LocationData("Rocket Hideout B2F", "Rocket", None, rom_addresses["Trainersanity_EVENT_BEAT_ROCKET_HIDEOUT_2_TRAINER_0_ITEM"], EventFlag(94), inclusion=trainersanity), + LocationData("Rocket Hideout B3F", "Rocket 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROCKET_HIDEOUT_3_TRAINER_1_ITEM"], EventFlag(92), inclusion=trainersanity), + LocationData("Rocket Hideout B3F", "Rocket 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROCKET_HIDEOUT_3_TRAINER_0_ITEM"], EventFlag(93), inclusion=trainersanity), + LocationData("Rocket Hideout B4F", "Rocket 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROCKET_HIDEOUT_4_TRAINER_2_ITEM"], EventFlag(79), inclusion=trainersanity), + LocationData("Rocket Hideout B4F", "Rocket 2 (Lift Key)", None, rom_addresses["Trainersanity_EVENT_BEAT_ROCKET_HIDEOUT_4_TRAINER_0_ITEM"], EventFlag(91), inclusion=trainersanity), + LocationData("Rocket Hideout B4F", "Rocket 3 (Lift Key)", None, rom_addresses["Trainersanity_EVENT_BEAT_ROCKET_HIDEOUT_4_TRAINER_1_ITEM"], EventFlag(90), inclusion=trainersanity), + LocationData("S.S. Anne 1F", "Gentleman 1", None, rom_addresses["Trainersanity_EVENT_BEAT_SS_ANNE_8_TRAINER_1_ITEM"], EventFlag(121), inclusion=trainersanity), + LocationData("S.S. Anne 1F", "Gentleman 2", None, rom_addresses["Trainersanity_EVENT_BEAT_SS_ANNE_8_TRAINER_0_ITEM"], EventFlag(122), inclusion=trainersanity), + LocationData("S.S. Anne 1F", "Lass", None, rom_addresses["Trainersanity_EVENT_BEAT_SS_ANNE_8_TRAINER_3_ITEM"], EventFlag(117), inclusion=trainersanity), + LocationData("S.S. Anne 1F", "Youngster", None, rom_addresses["Trainersanity_EVENT_BEAT_SS_ANNE_8_TRAINER_2_ITEM"], EventFlag(120), inclusion=trainersanity), + LocationData("S.S. Anne 2F", "Fisherman", None, rom_addresses["Trainersanity_EVENT_BEAT_SS_ANNE_9_TRAINER_1_ITEM"], EventFlag(115), inclusion=trainersanity), + LocationData("S.S. Anne 2F", "Gentleman 1", None, rom_addresses["Trainersanity_EVENT_BEAT_SS_ANNE_9_TRAINER_0_ITEM"], EventFlag(116), inclusion=trainersanity), + LocationData("S.S. Anne 2F", "Gentleman 2", None, rom_addresses["Trainersanity_EVENT_BEAT_SS_ANNE_9_TRAINER_2_ITEM"], EventFlag(113), inclusion=trainersanity), + LocationData("S.S. Anne 2F", "Lass", None, rom_addresses["Trainersanity_EVENT_BEAT_SS_ANNE_9_TRAINER_3_ITEM"], EventFlag(112), inclusion=trainersanity), + LocationData("S.S. Anne 3F", "Sailor 1", None, rom_addresses["Trainersanity_EVENT_BEAT_SS_ANNE_5_TRAINER_1_ITEM"], EventFlag(123), inclusion=trainersanity), + LocationData("S.S. Anne 3F", "Sailor 2", None, rom_addresses["Trainersanity_EVENT_BEAT_SS_ANNE_5_TRAINER_0_ITEM"], EventFlag(124), inclusion=trainersanity), + LocationData("S.S. Anne B1F", "Sailor 1", None, rom_addresses["Trainersanity_EVENT_BEAT_SS_ANNE_10_TRAINER_1_ITEM"], EventFlag(110), inclusion=trainersanity), + LocationData("S.S. Anne B1F", "Sailor 2", None, rom_addresses["Trainersanity_EVENT_BEAT_SS_ANNE_10_TRAINER_0_ITEM"], EventFlag(111), inclusion=trainersanity), + LocationData("S.S. Anne B1F", "Sailor 3", None, rom_addresses["Trainersanity_EVENT_BEAT_SS_ANNE_10_TRAINER_3_ITEM"], EventFlag(108), inclusion=trainersanity), + LocationData("S.S. Anne B1F", "Sailor 4", None, rom_addresses["Trainersanity_EVENT_BEAT_SS_ANNE_10_TRAINER_2_ITEM"], EventFlag(109), inclusion=trainersanity), + LocationData("S.S. Anne B1F", "Fisherman", None, rom_addresses["Trainersanity_EVENT_BEAT_SS_ANNE_10_TRAINER_5_ITEM"], EventFlag(106), inclusion=trainersanity), + LocationData("S.S. Anne B1F", "Sailor 5", None, rom_addresses["Trainersanity_EVENT_BEAT_SS_ANNE_10_TRAINER_4_ITEM"], EventFlag(107), inclusion=trainersanity), + LocationData("Mt Moon 1F", "Bug Catcher 1", None, rom_addresses["Trainersanity_EVENT_BEAT_MT_MOON_1_TRAINER_5_ITEM"], EventFlag(131), inclusion=trainersanity), + LocationData("Mt Moon 1F", "Lass 1", None, rom_addresses["Trainersanity_EVENT_BEAT_MT_MOON_1_TRAINER_4_ITEM"], EventFlag(132), inclusion=trainersanity), + LocationData("Mt Moon 1F", "Super Nerd", None, rom_addresses["Trainersanity_EVENT_BEAT_MT_MOON_1_TRAINER_3_ITEM"], EventFlag(133), inclusion=trainersanity), + LocationData("Mt Moon 1F", "Bug Catcher 2", None, rom_addresses["Trainersanity_EVENT_BEAT_MT_MOON_1_TRAINER_6_ITEM"], EventFlag(130), inclusion=trainersanity), + LocationData("Mt Moon 1F", "Lass 2", None, rom_addresses["Trainersanity_EVENT_BEAT_MT_MOON_1_TRAINER_2_ITEM"], EventFlag(134), inclusion=trainersanity), + LocationData("Mt Moon 1F", "Youngster", None, rom_addresses["Trainersanity_EVENT_BEAT_MT_MOON_1_TRAINER_1_ITEM"], EventFlag(135), inclusion=trainersanity), + LocationData("Mt Moon 1F", "Hiker", None, rom_addresses["Trainersanity_EVENT_BEAT_MT_MOON_1_TRAINER_0_ITEM"], EventFlag(136), inclusion=trainersanity), + LocationData("Mt Moon B2F", "Rocket 1", None, rom_addresses["Trainersanity_EVENT_BEAT_MT_MOON_3_TRAINER_1_ITEM"], EventFlag(127), inclusion=trainersanity), + LocationData("Mt Moon B2F", "Rocket 2", None, rom_addresses["Trainersanity_EVENT_BEAT_MT_MOON_3_TRAINER_2_ITEM"], EventFlag(126), inclusion=trainersanity), + LocationData("Mt Moon B2F", "Rocket 3", None, rom_addresses["Trainersanity_EVENT_BEAT_MT_MOON_3_TRAINER_3_ITEM"], EventFlag(125), inclusion=trainersanity), + LocationData("Mt Moon B2F", "Rocket 4", None, rom_addresses["Trainersanity_EVENT_BEAT_MT_MOON_3_TRAINER_0_ITEM"], EventFlag(128), inclusion=trainersanity), + LocationData("Viridian Forest", "Bug Catcher 1", None, rom_addresses["Trainersanity_EVENT_BEAT_VIRIDIAN_FOREST_TRAINER_0_ITEM"], EventFlag(139), inclusion=trainersanity), + LocationData("Viridian Forest", "Bug Catcher 2", None, rom_addresses["Trainersanity_EVENT_BEAT_VIRIDIAN_FOREST_TRAINER_1_ITEM"], EventFlag(138), inclusion=trainersanity), + LocationData("Viridian Forest", "Bug Catcher 3", None, rom_addresses["Trainersanity_EVENT_BEAT_VIRIDIAN_FOREST_TRAINER_2_ITEM"], EventFlag(137), inclusion=trainersanity), + LocationData("Route 25", "Hiker 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_6_ITEM"], EventFlag(142), inclusion=trainersanity), + LocationData("Route 25", "Youngster 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_0_ITEM"], EventFlag(148), inclusion=trainersanity), + LocationData("Route 25", "Hiker 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_8_ITEM"], EventFlag(140), inclusion=trainersanity), + LocationData("Route 25", "Youngster 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_1_ITEM"], EventFlag(147), inclusion=trainersanity), + LocationData("Route 25", "Lass 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_3_ITEM"], EventFlag(145), inclusion=trainersanity), + LocationData("Route 25", "Hiker 3", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_7_ITEM"], EventFlag(141), inclusion=trainersanity), + LocationData("Route 25", "Jr. Trainer M", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_2_ITEM"], EventFlag(146), inclusion=trainersanity), + LocationData("Route 25", "Youngster 3", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_4_ITEM"], EventFlag(144), inclusion=trainersanity), + LocationData("Route 25", "Lass 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_5_ITEM"], EventFlag(143), inclusion=trainersanity), + LocationData("Route 24", "Bug Catcher", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_24_TRAINER_5_ITEM"], EventFlag(149), inclusion=trainersanity), + LocationData("Route 24", "Lass 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_24_TRAINER_4_ITEM"], EventFlag(150), inclusion=trainersanity), + LocationData("Route 24", "Youngster", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_24_TRAINER_3_ITEM"], EventFlag(151), inclusion=trainersanity), + LocationData("Route 24", "Lass 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_24_TRAINER_2_ITEM"], EventFlag(153), inclusion=trainersanity), + LocationData("Route 24", "Jr. Trainer M 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_24_TRAINER_1_ITEM"], EventFlag(154), inclusion=trainersanity), + LocationData("Route 24", "Jr. Trainer M 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_24_TRAINER_0_ITEM"], EventFlag(155), inclusion=trainersanity), + LocationData("Route 21", "Fisherman 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_21_TRAINER_0_ITEM"], EventFlag(174), inclusion=trainersanity), + LocationData("Route 21", "Fisherman 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_21_TRAINER_1_ITEM"], EventFlag(173), inclusion=trainersanity), + LocationData("Route 21", "Cue Ball", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_21_TRAINER_3_ITEM"], EventFlag(171), inclusion=trainersanity), + LocationData("Route 21", "Swimmer 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_21_TRAINER_2_ITEM"], EventFlag(172), inclusion=trainersanity), + LocationData("Route 21", "Fisherman 3", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_21_TRAINER_7_ITEM"], EventFlag(166), inclusion=trainersanity), + LocationData("Route 21", "Fisherman 4", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_21_TRAINER_8_ITEM"], EventFlag(165), inclusion=trainersanity), + LocationData("Route 21", "Swimmer 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_21_TRAINER_4_ITEM"], EventFlag(170), inclusion=trainersanity), + LocationData("Route 21", "Swimmer 3", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_21_TRAINER_6_ITEM"], EventFlag(168), inclusion=trainersanity), + LocationData("Route 21", "Swimmer 4", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_21_TRAINER_5_ITEM"], EventFlag(169), inclusion=trainersanity), + LocationData("Route 20 West", "Beauty 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_9_ITEM"], EventFlag(175), inclusion=trainersanity), + LocationData("Route 20 West", "Jr. Trainer F 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_8_ITEM"], EventFlag(176), inclusion=trainersanity), + LocationData("Route 20 West", "Beauty 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_7_ITEM"], EventFlag(177), inclusion=trainersanity), + LocationData("Route 20 West", "Cooltrainer M", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_6_ITEM"], EventFlag(178), inclusion=trainersanity), + LocationData("Route 20 West", "Swimmer 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_4_ITEM"], EventFlag(180), inclusion=trainersanity), + LocationData("Route 20 West", "Jr. Trainer F 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_3_ITEM"], EventFlag(181), inclusion=trainersanity), + LocationData("Route 20 East", "Beauty 3", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_2_ITEM"], EventFlag(182), inclusion=trainersanity), + LocationData("Route 20 East", "Beauty 4", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_1_ITEM"], EventFlag(183), inclusion=trainersanity), + LocationData("Route 20 East", "Swimmer 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_5_ITEM"], EventFlag(179), inclusion=trainersanity), + LocationData("Route 20 East", "Swimmer 3", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_0_ITEM"], EventFlag(184), inclusion=trainersanity), + LocationData("Route 19", "Swimmer 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_19_TRAINER_0_ITEM"], EventFlag(199), inclusion=trainersanity), + LocationData("Route 19", "Swimmer 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_19_TRAINER_1_ITEM"], EventFlag(198), inclusion=trainersanity), + LocationData("Route 19", "Swimmer 3", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_19_TRAINER_5_ITEM"], EventFlag(194), inclusion=trainersanity), + LocationData("Route 19", "Swimmer 4", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_19_TRAINER_2_ITEM"], EventFlag(197), inclusion=trainersanity), + LocationData("Route 19", "Swimmer 5", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_19_TRAINER_3_ITEM"], EventFlag(196), inclusion=trainersanity), + LocationData("Route 19", "Swimmer 6", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_19_TRAINER_4_ITEM"], EventFlag(195), inclusion=trainersanity), + LocationData("Route 19", "Swimmer 7", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_19_TRAINER_8_ITEM"], EventFlag(188), inclusion=trainersanity), + LocationData("Route 19", "Beauty 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_19_TRAINER_7_ITEM"], EventFlag(189), inclusion=trainersanity), + LocationData("Route 19", "Beauty 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_19_TRAINER_6_ITEM"], EventFlag(193), inclusion=trainersanity), + LocationData("Route 19", "Beauty 3", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_19_TRAINER_9_ITEM"], EventFlag(185), inclusion=trainersanity), + LocationData("Route 18", "Bird Keeper 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_18_TRAINER_2_ITEM"], EventFlag(200), inclusion=trainersanity), + LocationData("Route 18", "Bird Keeper 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_18_TRAINER_1_ITEM"], EventFlag(201), inclusion=trainersanity), + LocationData("Route 18", "Bird Keeper 3", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_18_TRAINER_0_ITEM"], EventFlag(202), inclusion=trainersanity), + LocationData("Route 17", "Cue Ball 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_17_TRAINER_1_ITEM"], EventFlag(211), inclusion=trainersanity), + LocationData("Route 17", "Biker 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_17_TRAINER_2_ITEM"], EventFlag(210), inclusion=trainersanity), + LocationData("Route 17", "Cue Ball 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_17_TRAINER_0_ITEM"], EventFlag(212), inclusion=trainersanity), + LocationData("Route 17", "Biker 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_17_TRAINER_3_ITEM"], EventFlag(209), inclusion=trainersanity), + LocationData("Route 17", "Biker 3", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_17_TRAINER_4_ITEM"], EventFlag(208), inclusion=trainersanity), + LocationData("Route 17", "Cue Ball 3", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_17_TRAINER_5_ITEM"], EventFlag(207), inclusion=trainersanity), + LocationData("Route 17", "Cue Ball 4", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_17_TRAINER_6_ITEM"], EventFlag(206), inclusion=trainersanity), + LocationData("Route 17", "Biker 4", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_17_TRAINER_8_ITEM"], EventFlag(204), inclusion=trainersanity), + LocationData("Route 17", "Cue Ball 5", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_17_TRAINER_7_ITEM"], EventFlag(205), inclusion=trainersanity), + LocationData("Route 17", "Biker 5", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_17_TRAINER_9_ITEM"], EventFlag(203), inclusion=trainersanity), + LocationData("Route 16 West", "Biker", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_16_TRAINER_0_ITEM"], EventFlag(219), inclusion=trainersanity), + LocationData("Route 16 West", "Cue Ball 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_16_TRAINER_1_ITEM"], EventFlag(218), inclusion=trainersanity), + LocationData("Route 16 West", "Cue Ball 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_16_TRAINER_2_ITEM"], EventFlag(217), inclusion=trainersanity), + LocationData("Route 16 West", "Biker 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_16_TRAINER_3_ITEM"], EventFlag(216), inclusion=trainersanity), + LocationData("Route 16 West", "Cue Ball 3", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_16_TRAINER_4_ITEM"], EventFlag(215), inclusion=trainersanity), + LocationData("Route 16 West", "Biker 3", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_16_TRAINER_5_ITEM"], EventFlag(214), inclusion=trainersanity), + LocationData("Route 15", "Jr. Trainer F 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_15_TRAINER_8_ITEM"], EventFlag(221), inclusion=trainersanity), + LocationData("Route 15", "Beauty 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_15_TRAINER_4_ITEM"], EventFlag(225), inclusion=trainersanity), + LocationData("Route 15", "Jr. Trainer F 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_15_TRAINER_1_ITEM"], EventFlag(228), inclusion=trainersanity), + LocationData("Route 15", "Biker 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_15_TRAINER_6_ITEM"], EventFlag(223), inclusion=trainersanity), + LocationData("Route 15", "Biker 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_15_TRAINER_7_ITEM"], EventFlag(222), inclusion=trainersanity), + LocationData("Route 15", "Jr. Trainer F 3", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_15_TRAINER_0_ITEM"], EventFlag(229), inclusion=trainersanity), + LocationData("Route 15", "Beauty 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_15_TRAINER_5_ITEM"], EventFlag(224), inclusion=trainersanity), + LocationData("Route 15", "Bird Keeper 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_15_TRAINER_3_ITEM"], EventFlag(226), inclusion=trainersanity), + LocationData("Route 15", "Bird Keeper 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_15_TRAINER_2_ITEM"], EventFlag(227), inclusion=trainersanity), + LocationData("Route 15", "Jr. Trainer F 4", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_15_TRAINER_9_ITEM"], EventFlag(220), inclusion=trainersanity), + LocationData("Route 14", "Bird Keeper 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_14_TRAINER_0_ITEM"], EventFlag(244), inclusion=trainersanity), + LocationData("Route 14", "Bird Keeper 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_14_TRAINER_1_ITEM"], EventFlag(240), inclusion=trainersanity), + LocationData("Route 14", "Bird Keeper 3", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_14_TRAINER_2_ITEM"], EventFlag(237), inclusion=trainersanity), + LocationData("Route 14", "Bird Keeper 4", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_14_TRAINER_3_ITEM"], EventFlag(236), inclusion=trainersanity), + LocationData("Route 14", "Biker 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_14_TRAINER_8_ITEM"], EventFlag(231), inclusion=trainersanity), + LocationData("Route 14", "Bird Keeper 5", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_14_TRAINER_4_ITEM"], EventFlag(235), inclusion=trainersanity), + LocationData("Route 14", "Biker 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_14_TRAINER_9_ITEM"], EventFlag(230), inclusion=trainersanity), + LocationData("Route 14", "Biker 3", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_14_TRAINER_7_ITEM"], EventFlag(232), inclusion=trainersanity), + LocationData("Route 14", "Biker 4", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_14_TRAINER_6_ITEM"], EventFlag(233), inclusion=trainersanity), + LocationData("Route 14", "Bird Keeper 6", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_14_TRAINER_5_ITEM"], EventFlag(234), inclusion=trainersanity), + LocationData("Route 13 East", "Jr. Trainer F 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_13_TRAINER_4_ITEM"], EventFlag(253), inclusion=trainersanity), + LocationData("Route 13 East", "Bird Keeper 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_13_TRAINER_0_ITEM"], EventFlag(257), inclusion=trainersanity), + LocationData("Route 13 East", "Jr. Trainer F 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_13_TRAINER_1_ITEM"], EventFlag(256), inclusion=trainersanity), + LocationData("Route 13", "Beauty 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_13_TRAINER_6_ITEM"], EventFlag(248), inclusion=trainersanity), + LocationData("Route 13", "Beauty 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_13_TRAINER_7_ITEM"], EventFlag(247), inclusion=trainersanity), + LocationData("Route 13", "Jr. Trainer F 3", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_13_TRAINER_2_ITEM"], EventFlag(255), inclusion=trainersanity), + LocationData("Route 13", "Jr. Trainer F 4", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_13_TRAINER_3_ITEM"], EventFlag(254), inclusion=trainersanity), + LocationData("Route 13", "Bird Keeper 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_13_TRAINER_9_ITEM"], EventFlag(245), inclusion=trainersanity), + LocationData("Route 13", "Bird Keeper 3", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_13_TRAINER_5_ITEM"], EventFlag(252), inclusion=trainersanity), + LocationData("Route 13", "Biker", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_13_TRAINER_8_ITEM"], EventFlag(246), inclusion=trainersanity), + LocationData("Route 12 North", "Fisherman 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_12_TRAINER_0_ITEM"], EventFlag(277), inclusion=trainersanity), + LocationData("Route 12 North", "Fisherman 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_12_TRAINER_1_ITEM"], EventFlag(276), inclusion=trainersanity), + LocationData("Route 12 North", "Fisherman 3", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_12_TRAINER_4_ITEM"], EventFlag(269), inclusion=trainersanity), + LocationData("Route 12 North", "Fisherman 4", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_12_TRAINER_5_ITEM"], EventFlag(268), inclusion=trainersanity), + LocationData("Route 12 South", "Rocker", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_12_TRAINER_3_ITEM"], EventFlag(270), inclusion=trainersanity), + LocationData("Route 12 South", "Jr. Trainer M", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_12_TRAINER_2_ITEM"], EventFlag(272), inclusion=trainersanity), + LocationData("Route 12 Grass", "Fisherman 5", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_12_TRAINER_6_ITEM"], EventFlag(264), inclusion=trainersanity), + LocationData("Route 11", "Youngster 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_11_TRAINER_2_ITEM"], EventFlag(286), inclusion=trainersanity), + LocationData("Route 11", "Gambler 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_11_TRAINER_0_ITEM"], EventFlag(288), inclusion=trainersanity), + LocationData("Route 11", "Youngster 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_11_TRAINER_9_ITEM"], EventFlag(278), inclusion=trainersanity), + LocationData("Route 11", "Youngster 3", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_11_TRAINER_4_ITEM"], EventFlag(284), inclusion=trainersanity), + LocationData("Route 11", "Gambler 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_11_TRAINER_1_ITEM"], EventFlag(287), inclusion=trainersanity), + LocationData("Route 11", "Gambler 3", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_11_TRAINER_6_ITEM"], EventFlag(282), inclusion=trainersanity), + LocationData("Route 11", "Engineer 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_11_TRAINER_3_ITEM"], EventFlag(285), inclusion=trainersanity), + LocationData("Route 11", "Engineer 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_11_TRAINER_8_ITEM"], EventFlag(280), inclusion=trainersanity), + LocationData("Route 11", "Youngster 4", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_11_TRAINER_7_ITEM"], EventFlag(281), inclusion=trainersanity), + LocationData("Route 11", "Gambler 4", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_11_TRAINER_5_ITEM"], EventFlag(283), inclusion=trainersanity), + LocationData("Rock Tunnel 1F", "PokeManiac", None, rom_addresses["Trainersanity_EVENT_BEAT_ROCK_TUNNEL_1_TRAINER_3_ITEM"], EventFlag(302), inclusion=trainersanity), + LocationData("Rock Tunnel 1F", "Hiker 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROCK_TUNNEL_1_TRAINER_0_ITEM"], EventFlag(305), inclusion=trainersanity), + LocationData("Rock Tunnel 1F", "Hiker 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROCK_TUNNEL_1_TRAINER_1_ITEM"], EventFlag(304), inclusion=trainersanity), + LocationData("Rock Tunnel 1F", "Hiker 3", None, rom_addresses["Trainersanity_EVENT_BEAT_ROCK_TUNNEL_1_TRAINER_2_ITEM"], EventFlag(303), inclusion=trainersanity), + LocationData("Rock Tunnel 1F", "Jr. Trainer F 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROCK_TUNNEL_1_TRAINER_4_ITEM"], EventFlag(301), inclusion=trainersanity), + LocationData("Rock Tunnel 1F", "Jr. Trainer F 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROCK_TUNNEL_1_TRAINER_6_ITEM"], EventFlag(299), inclusion=trainersanity), + LocationData("Rock Tunnel 1F", "Jr. Trainer F 3", None, rom_addresses["Trainersanity_EVENT_BEAT_ROCK_TUNNEL_1_TRAINER_5_ITEM"], EventFlag(300), inclusion=trainersanity), + LocationData("Rock Tunnel B1F", "PokeManiac 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROCK_TUNNEL_2_TRAINER_7_ITEM"], EventFlag(5), inclusion=trainersanity), + LocationData("Rock Tunnel B1F", "Jr. Trainer F 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROCK_TUNNEL_2_TRAINER_5_ITEM"], EventFlag(8), inclusion=trainersanity), + LocationData("Rock Tunnel B1F", "PokeManiac 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROCK_TUNNEL_2_TRAINER_3_ITEM"], EventFlag(10), inclusion=trainersanity), + LocationData("Rock Tunnel B1F", "Hiker 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROCK_TUNNEL_2_TRAINER_6_ITEM"], EventFlag(7), inclusion=trainersanity), + LocationData("Rock Tunnel B1F", "Hiker 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROCK_TUNNEL_2_TRAINER_4_ITEM"], EventFlag(9), inclusion=trainersanity), + LocationData("Rock Tunnel B1F", "Jr. Trainer F 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROCK_TUNNEL_2_TRAINER_0_ITEM"], EventFlag(13), inclusion=trainersanity), + LocationData("Rock Tunnel B1F", "Hiker 3", None, rom_addresses["Trainersanity_EVENT_BEAT_ROCK_TUNNEL_2_TRAINER_1_ITEM"], EventFlag(12), inclusion=trainersanity), + LocationData("Rock Tunnel B1F", "PokeManiac 3", None, rom_addresses["Trainersanity_EVENT_BEAT_ROCK_TUNNEL_2_TRAINER_2_ITEM"], EventFlag(11), inclusion=trainersanity), + LocationData("Route 10 North", "Jr. Trainer F 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_10_TRAINER_3_ITEM"], EventFlag(308), inclusion=trainersanity), + LocationData("Route 10 North", "PokeManiac", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_10_TRAINER_0_ITEM"], EventFlag(311), inclusion=trainersanity), + LocationData("Route 10 South", "J.r Trainer F 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_10_TRAINER_5_ITEM"], EventFlag(306), inclusion=trainersanity), + LocationData("Route 10 South", "Hiker 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_10_TRAINER_1_ITEM"], EventFlag(310), inclusion=trainersanity), + LocationData("Route 10 South", "Hiker 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_10_TRAINER_4_ITEM"], EventFlag(307), inclusion=trainersanity), + LocationData("Route 10 South", "Super Nerd", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_10_TRAINER_2_ITEM"], EventFlag(309), inclusion=trainersanity), + LocationData("Route 9", "Jr. Trainer F 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_9_TRAINER_0_ITEM"], EventFlag(320), inclusion=trainersanity), + LocationData("Route 9", "Hiker 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_9_TRAINER_4_ITEM"], EventFlag(316), inclusion=trainersanity), + LocationData("Route 9", "Jr. Trainer M 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_9_TRAINER_1_ITEM"], EventFlag(319), inclusion=trainersanity), + LocationData("Route 9", "Bug Catcher 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_9_TRAINER_6_ITEM"], EventFlag(314), inclusion=trainersanity), + LocationData("Route 9", "Hiker 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_9_TRAINER_7_ITEM"], EventFlag(313), inclusion=trainersanity), + LocationData("Route 9", "Bug Catcher 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_9_TRAINER_8_ITEM"], EventFlag(312), inclusion=trainersanity), + LocationData("Route 9", "Jr. Trainer M 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_9_TRAINER_2_ITEM"], EventFlag(318), inclusion=trainersanity), + LocationData("Route 9", "Hiker 3", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_9_TRAINER_5_ITEM"], EventFlag(315), inclusion=trainersanity), + LocationData("Route 9", "Jr. Trainer F 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_9_TRAINER_3_ITEM"], EventFlag(317), inclusion=trainersanity), + LocationData("Route 8", "Lass 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_8_TRAINER_8_ITEM"], EventFlag(321), inclusion=trainersanity), + LocationData("Route 8", "Gambler 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_8_TRAINER_7_ITEM"], EventFlag(322), inclusion=trainersanity), + LocationData("Route 8", "Super Nerd 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_8_TRAINER_2_ITEM"], EventFlag(327), inclusion=trainersanity), + LocationData("Route 8", "Lass 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_8_TRAINER_3_ITEM"], EventFlag(326), inclusion=trainersanity), + LocationData("Route 8", "Super Nerd 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_8_TRAINER_4_ITEM"], EventFlag(325), inclusion=trainersanity), + LocationData("Route 8", "Lass 3", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_8_TRAINER_5_ITEM"], EventFlag(324), inclusion=trainersanity), + LocationData("Route 8", "Lass 4", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_8_TRAINER_6_ITEM"], EventFlag(323), inclusion=trainersanity), + LocationData("Route 8", "Gambler 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_8_TRAINER_1_ITEM"], EventFlag(328), inclusion=trainersanity), + LocationData("Route 8", "Super Nerd 3", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_8_TRAINER_0_ITEM"], EventFlag(329), inclusion=trainersanity), + LocationData("Route 6", "Bug Catcher 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_6_TRAINER_2_ITEM"], EventFlag(333), inclusion=trainersanity), + LocationData("Route 6", "Jr. Trainer M 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_6_TRAINER_1_ITEM"], EventFlag(334), inclusion=trainersanity), + LocationData("Route 6", "Jr. Trainer F 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_6_TRAINER_0_ITEM"], EventFlag(335), inclusion=trainersanity), + LocationData("Route 6", "Bug Catcher 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_6_TRAINER_5_ITEM"], EventFlag(330), inclusion=trainersanity), + LocationData("Route 6", "Jr. Trainer F 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_6_TRAINER_4_ITEM"], EventFlag(331), inclusion=trainersanity), + LocationData("Route 6", "Jr. Trainer M 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_6_TRAINER_3_ITEM"], EventFlag(332), inclusion=trainersanity), + LocationData("Route 4", "Cooltrainer F", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_4_TRAINER_0_ITEM"], EventFlag(336), inclusion=trainersanity), + LocationData("Route 3", "Lass 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_3_TRAINER_2_ITEM"], EventFlag(345), inclusion=trainersanity), + LocationData("Route 3", "Bug Catcher 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_3_TRAINER_0_ITEM"], EventFlag(347), inclusion=trainersanity), + LocationData("Route 3", "Youngster 1", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_3_TRAINER_1_ITEM"], EventFlag(346), inclusion=trainersanity), + LocationData("Route 3", "Bug Catcher 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_3_TRAINER_3_ITEM"], EventFlag(344), inclusion=trainersanity), + LocationData("Route 3", "Lass 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_3_TRAINER_4_ITEM"], EventFlag(341), inclusion=trainersanity), + LocationData("Route 3", "Youngster 2", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_3_TRAINER_5_ITEM"], EventFlag(340), inclusion=trainersanity), + LocationData("Route 3", "Bug Catcher 3", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_3_TRAINER_6_ITEM"], EventFlag(339), inclusion=trainersanity), + LocationData("Route 3", "Lass 3", None, rom_addresses["Trainersanity_EVENT_BEAT_ROUTE_3_TRAINER_7_ITEM"], EventFlag(338), inclusion=trainersanity), + LocationData("Saffron Gym", "Psychic 1", None, rom_addresses["Trainersanity_EVENT_BEAT_SAFFRON_GYM_TRAINER_5_ITEM"], EventFlag(349), inclusion=trainersanity), + LocationData("Saffron Gym", "Psychic 2", None, rom_addresses["Trainersanity_EVENT_BEAT_SAFFRON_GYM_TRAINER_3_ITEM"], EventFlag(351), inclusion=trainersanity), + LocationData("Saffron Gym", "Psychic 3", None, rom_addresses["Trainersanity_EVENT_BEAT_SAFFRON_GYM_TRAINER_1_ITEM"], EventFlag(360), inclusion=trainersanity), + LocationData("Saffron Gym", "Channeler 1", None, rom_addresses["Trainersanity_EVENT_BEAT_SAFFRON_GYM_TRAINER_0_ITEM"], EventFlag(361), inclusion=trainersanity), + LocationData("Saffron Gym", "Psychic 4", None, rom_addresses["Trainersanity_EVENT_BEAT_SAFFRON_GYM_TRAINER_6_ITEM"], EventFlag(348), inclusion=trainersanity), + LocationData("Saffron Gym", "Channeler 2", None, rom_addresses["Trainersanity_EVENT_BEAT_SAFFRON_GYM_TRAINER_2_ITEM"], EventFlag(357), inclusion=trainersanity), + LocationData("Saffron Gym", "Channeler 3", None, rom_addresses["Trainersanity_EVENT_BEAT_SAFFRON_GYM_TRAINER_4_ITEM"], EventFlag(350), inclusion=trainersanity), + LocationData("Fighting Dojo", "Blackbelt 1", None, rom_addresses["Trainersanity_EVENT_BEAT_FIGHTING_DOJO_TRAINER_3_ITEM"], EventFlag(363), inclusion=trainersanity), + LocationData("Fighting Dojo", "Blackbelt 2", None, rom_addresses["Trainersanity_EVENT_BEAT_FIGHTING_DOJO_TRAINER_1_ITEM"], EventFlag(365), inclusion=trainersanity), + LocationData("Fighting Dojo", "Blackbelt 3", None, rom_addresses["Trainersanity_EVENT_BEAT_FIGHTING_DOJO_TRAINER_2_ITEM"], EventFlag(364), inclusion=trainersanity), + LocationData("Fighting Dojo", "Blackbelt 4", None, rom_addresses["Trainersanity_EVENT_BEAT_FIGHTING_DOJO_TRAINER_0_ITEM"], EventFlag(366), inclusion=trainersanity), + LocationData("Fuchsia Gym", "Juggler 1", None, rom_addresses["Trainersanity_EVENT_BEAT_FUCHSIA_GYM_TRAINER_2_ITEM"], EventFlag(380), inclusion=trainersanity), + LocationData("Fuchsia Gym", "Juggler 2", None, rom_addresses["Trainersanity_EVENT_BEAT_FUCHSIA_GYM_TRAINER_0_ITEM"], EventFlag(382), inclusion=trainersanity), + LocationData("Fuchsia Gym", "Juggler 3", None, rom_addresses["Trainersanity_EVENT_BEAT_FUCHSIA_GYM_TRAINER_1_ITEM"], EventFlag(381), inclusion=trainersanity), + LocationData("Fuchsia Gym", "Tamer 1", None, rom_addresses["Trainersanity_EVENT_BEAT_FUCHSIA_GYM_TRAINER_4_ITEM"], EventFlag(378), inclusion=trainersanity), + LocationData("Fuchsia Gym", "Tamer 2", None, rom_addresses["Trainersanity_EVENT_BEAT_FUCHSIA_GYM_TRAINER_3_ITEM"], EventFlag(379), inclusion=trainersanity), + LocationData("Fuchsia Gym", "Juggler 4", None, rom_addresses["Trainersanity_EVENT_BEAT_FUCHSIA_GYM_TRAINER_5_ITEM"], EventFlag(377), inclusion=trainersanity), + LocationData("Celadon Gym", "Lass 1", None, rom_addresses["Trainersanity_EVENT_BEAT_CELADON_GYM_TRAINER_0_ITEM"], EventFlag(391), inclusion=trainersanity), + LocationData("Celadon Gym", "Beauty 1", None, rom_addresses["Trainersanity_EVENT_BEAT_CELADON_GYM_TRAINER_1_ITEM"], EventFlag(390), inclusion=trainersanity), + LocationData("Celadon Gym", "Beauty 2", None, rom_addresses["Trainersanity_EVENT_BEAT_CELADON_GYM_TRAINER_3_ITEM"], EventFlag(388), inclusion=trainersanity), + LocationData("Celadon Gym", "Jr. Trainer F", None, rom_addresses["Trainersanity_EVENT_BEAT_CELADON_GYM_TRAINER_2_ITEM"], EventFlag(389), inclusion=trainersanity), + LocationData("Celadon Gym", "Lass 2", None, rom_addresses["Trainersanity_EVENT_BEAT_CELADON_GYM_TRAINER_4_ITEM"], EventFlag(387), inclusion=trainersanity), + LocationData("Celadon Gym", "Cool Trainer F", None, rom_addresses["Trainersanity_EVENT_BEAT_CELADON_GYM_TRAINER_6_ITEM"], EventFlag(385), inclusion=trainersanity), + LocationData("Celadon Gym", "Beauty 3", None, rom_addresses["Trainersanity_EVENT_BEAT_CELADON_GYM_TRAINER_5_ITEM"], EventFlag(386), inclusion=trainersanity), + LocationData("Vermilion Gym", "Sailor", None, rom_addresses["Trainersanity_EVENT_BEAT_VERMILION_GYM_TRAINER_2_ITEM"], EventFlag(394), inclusion=trainersanity), + LocationData("Vermilion Gym", "Rocker", None, rom_addresses["Trainersanity_EVENT_BEAT_VERMILION_GYM_TRAINER_1_ITEM"], EventFlag(395), inclusion=trainersanity), + LocationData("Vermilion Gym", "Gentleman", None, rom_addresses["Trainersanity_EVENT_BEAT_VERMILION_GYM_TRAINER_0_ITEM"], EventFlag(400), inclusion=trainersanity), + LocationData("Pokemon Tower 3F", "Channeler 1", None, rom_addresses["Trainersanity_EVENT_BEAT_POKEMONTOWER_3_TRAINER_0_ITEM"], EventFlag(417), inclusion=trainersanity), + LocationData("Pokemon Tower 3F", "Channeler 2", None, rom_addresses["Trainersanity_EVENT_BEAT_POKEMONTOWER_3_TRAINER_1_ITEM"], EventFlag(416), inclusion=trainersanity), + LocationData("Pokemon Tower 3F", "Channeler 3", None, rom_addresses["Trainersanity_EVENT_BEAT_POKEMONTOWER_3_TRAINER_2_ITEM"], EventFlag(415), inclusion=trainersanity), + LocationData("Pokemon Tower 4F", "Channeler 1", None, rom_addresses["Trainersanity_EVENT_BEAT_POKEMONTOWER_4_TRAINER_1_ITEM"], EventFlag(413), inclusion=trainersanity), + LocationData("Pokemon Tower 4F", "Channeler 2", None, rom_addresses["Trainersanity_EVENT_BEAT_POKEMONTOWER_4_TRAINER_2_ITEM"], EventFlag(412), inclusion=trainersanity), + LocationData("Pokemon Tower 4F", "Channeler 3", None, rom_addresses["Trainersanity_EVENT_BEAT_POKEMONTOWER_4_TRAINER_0_ITEM"], EventFlag(414), inclusion=trainersanity), + LocationData("Pokemon Tower 5F", "Channeler 1", None, rom_addresses["Trainersanity_EVENT_BEAT_POKEMONTOWER_5_TRAINER_1_ITEM"], EventFlag(410), inclusion=trainersanity), + LocationData("Pokemon Tower 5F", "Channeler 2", None, rom_addresses["Trainersanity_EVENT_BEAT_POKEMONTOWER_5_TRAINER_0_ITEM"], EventFlag(411), inclusion=trainersanity), + LocationData("Pokemon Tower 5F", "Channeler 3", None, rom_addresses["Trainersanity_EVENT_BEAT_POKEMONTOWER_5_TRAINER_2_ITEM"], EventFlag(409), inclusion=trainersanity), + LocationData("Pokemon Tower 5F", "Channeler 4", None, rom_addresses["Trainersanity_EVENT_BEAT_POKEMONTOWER_5_TRAINER_3_ITEM"], EventFlag(408), inclusion=trainersanity), + LocationData("Pokemon Tower 6F", "Channeler 1", None, rom_addresses["Trainersanity_EVENT_BEAT_POKEMONTOWER_6_TRAINER_0_ITEM"], EventFlag(407), inclusion=trainersanity), + LocationData("Pokemon Tower 6F", "Channeler 2", None, rom_addresses["Trainersanity_EVENT_BEAT_POKEMONTOWER_6_TRAINER_2_ITEM"], EventFlag(405), inclusion=trainersanity), + LocationData("Pokemon Tower 6F", "Channeler 3", None, rom_addresses["Trainersanity_EVENT_BEAT_POKEMONTOWER_6_TRAINER_1_ITEM"], EventFlag(406), inclusion=trainersanity), + LocationData("Pokemon Tower 7F", "Rocket 1", None, rom_addresses["Trainersanity_EVENT_BEAT_POKEMONTOWER_7_TRAINER_0_ITEM"], EventFlag(403), inclusion=trainersanity), + LocationData("Pokemon Tower 7F", "Rocket 2", None, rom_addresses["Trainersanity_EVENT_BEAT_POKEMONTOWER_7_TRAINER_1_ITEM"], EventFlag(402), inclusion=trainersanity), + LocationData("Pokemon Tower 7F", "Rocket 3", None, rom_addresses["Trainersanity_EVENT_BEAT_POKEMONTOWER_7_TRAINER_2_ITEM"], EventFlag(401), inclusion=trainersanity), + LocationData("Cerulean Gym", "Swimmer", None, rom_addresses["Trainersanity_EVENT_BEAT_CERULEAN_GYM_TRAINER_1_ITEM"], EventFlag(420), inclusion=trainersanity), + LocationData("Cerulean Gym", "Jr. Trainer F", None, rom_addresses["Trainersanity_EVENT_BEAT_CERULEAN_GYM_TRAINER_0_ITEM"], EventFlag(421), inclusion=trainersanity), + LocationData("Pewter Gym", "Jr. Trainer M", None, rom_addresses["Trainersanity_EVENT_BEAT_PEWTER_GYM_TRAINER_0_ITEM"], EventFlag(434), inclusion=trainersanity), + LocationData("Viridian Gym", "Tamer 1", None, rom_addresses["Trainersanity_EVENT_BEAT_VIRIDIAN_GYM_TRAINER_6_ITEM"], EventFlag(436), inclusion=trainersanity), + LocationData("Viridian Gym", "Blackbelt 1", None, rom_addresses["Trainersanity_EVENT_BEAT_VIRIDIAN_GYM_TRAINER_3_ITEM"], EventFlag(439), inclusion=trainersanity), + LocationData("Viridian Gym", "Cooltrainer M 1", None, rom_addresses["Trainersanity_EVENT_BEAT_VIRIDIAN_GYM_TRAINER_7_ITEM"], EventFlag(435), inclusion=trainersanity), + LocationData("Viridian Gym", "Cooltrainer M 2", None, rom_addresses["Trainersanity_EVENT_BEAT_VIRIDIAN_GYM_TRAINER_0_ITEM"], EventFlag(446), inclusion=trainersanity), + LocationData("Viridian Gym", "Blackbelt 2", None, rom_addresses["Trainersanity_EVENT_BEAT_VIRIDIAN_GYM_TRAINER_1_ITEM"], EventFlag(445), inclusion=trainersanity), + LocationData("Viridian Gym", "Tamer 2", None, rom_addresses["Trainersanity_EVENT_BEAT_VIRIDIAN_GYM_TRAINER_2_ITEM"], EventFlag(440), inclusion=trainersanity), + LocationData("Viridian Gym", "Cooltrainer M 3", None, rom_addresses["Trainersanity_EVENT_BEAT_VIRIDIAN_GYM_TRAINER_5_ITEM"], EventFlag(437), inclusion=trainersanity), + LocationData("Viridian Gym", "Blackbelt 3", None, rom_addresses["Trainersanity_EVENT_BEAT_VIRIDIAN_GYM_TRAINER_4_ITEM"], EventFlag(438), inclusion=trainersanity), + LocationData("Victory Road 1F", "Cooltrainer F", None, rom_addresses["Trainersanity_EVENT_BEAT_VICTORY_ROAD_1_TRAINER_0_ITEM"], EventFlag(15), inclusion=trainersanity), + LocationData("Victory Road 1F", "Cooltrainer M", None, rom_addresses["Trainersanity_EVENT_BEAT_VICTORY_ROAD_1_TRAINER_1_ITEM"], EventFlag(14), inclusion=trainersanity), + LocationData("Victory Road 2F", "Blackbelt", None, rom_addresses["Trainersanity_EVENT_BEAT_VICTORY_ROAD_2_TRAINER_0_ITEM"], EventFlag(162), inclusion=trainersanity), + LocationData("Victory Road 2F", "Juggler 1", None, rom_addresses["Trainersanity_EVENT_BEAT_VICTORY_ROAD_2_TRAINER_1_ITEM"], EventFlag(161), inclusion=trainersanity), + LocationData("Victory Road 2F", "Tamer", None, rom_addresses["Trainersanity_EVENT_BEAT_VICTORY_ROAD_2_TRAINER_2_ITEM"], EventFlag(160), inclusion=trainersanity), + LocationData("Victory Road 2F", "Juggler 2", None, rom_addresses["Trainersanity_EVENT_BEAT_VICTORY_ROAD_2_TRAINER_4_ITEM"], EventFlag(158), inclusion=trainersanity), + LocationData("Victory Road 2F", "PokeManiac", None, rom_addresses["Trainersanity_EVENT_BEAT_VICTORY_ROAD_2_TRAINER_3_ITEM"], EventFlag(159), inclusion=trainersanity), + LocationData("Victory Road 3F", "Cooltrainer M 1", None, rom_addresses["Trainersanity_EVENT_BEAT_VICTORY_ROAD_3_TRAINER_0_ITEM"], EventFlag(103), inclusion=trainersanity), + LocationData("Victory Road 3F", "Cooltrainer F 1", None, rom_addresses["Trainersanity_EVENT_BEAT_VICTORY_ROAD_3_TRAINER_3_ITEM"], EventFlag(100), inclusion=trainersanity), + LocationData("Victory Road 3F", "Cooltrainer M 2", None, rom_addresses["Trainersanity_EVENT_BEAT_VICTORY_ROAD_3_TRAINER_2_ITEM"], EventFlag(101), inclusion=trainersanity), + LocationData("Victory Road 3F", "Cooltrainer F 2", None, rom_addresses["Trainersanity_EVENT_BEAT_VICTORY_ROAD_3_TRAINER_1_ITEM"], EventFlag(102), inclusion=trainersanity), + LocationData("Indigo Plateau", "Lorelei", None, rom_addresses["Trainersanity_EVENT_BEAT_LORELEIS_ROOM_TRAINER_0_ITEM"], EventFlag(21), inclusion=trainersanity), + LocationData("Indigo Plateau", "Bruno", None, rom_addresses["Trainersanity_EVENT_BEAT_BRUNOS_ROOM_TRAINER_0_ITEM"], EventFlag(20), inclusion=trainersanity), + LocationData("Indigo Plateau", "Agatha", None, rom_addresses["Trainersanity_EVENT_BEAT_AGATHAS_ROOM_TRAINER_0_ITEM"], EventFlag(19), inclusion=trainersanity), + LocationData("Indigo Plateau", "Lance", None, rom_addresses["Trainersanity_EVENT_BEAT_LANCES_ROOM_TRAINER_0_ITEM"], EventFlag(18), inclusion=trainersanity), LocationData("Indigo Plateau", "Become Champion", "Become Champion", event=True), LocationData("Pokemon Tower 7F", "Fuji Saved", "Fuji Saved", event=True), @@ -977,25 +1318,25 @@ def __init__(self, flag): event=True, type="Wild Encounter"), LocationData("Route 13", "Wild Pokemon - 10", ["Gloom", "Weepinbell"], rom_addresses["Wild_Route13"] + 19, None, event=True, type="Wild Encounter"), - LocationData("Route 14", "Wild Pokemon - 1", ["Oddish", "Bellsprout"], rom_addresses["Wild_Route14"] + 1, None, + LocationData("Route 14 Grass", "Wild Pokemon - 1", ["Oddish", "Bellsprout"], rom_addresses["Wild_Route14"] + 1, None, event=True, type="Wild Encounter"), - LocationData("Route 14", "Wild Pokemon - 2", "Pidgey", rom_addresses["Wild_Route14"] + 3, None, event=True, + LocationData("Route 14 Grass", "Wild Pokemon - 2", "Pidgey", rom_addresses["Wild_Route14"] + 3, None, event=True, type="Wild Encounter"), - LocationData("Route 14", "Wild Pokemon - 3", "Ditto", rom_addresses["Wild_Route14"] + 5, None, event=True, + LocationData("Route 14 Grass", "Wild Pokemon - 3", "Ditto", rom_addresses["Wild_Route14"] + 5, None, event=True, type="Wild Encounter"), - LocationData("Route 14", "Wild Pokemon - 4", "Venonat", rom_addresses["Wild_Route14"] + 7, None, event=True, + LocationData("Route 14 Grass", "Wild Pokemon - 4", "Venonat", rom_addresses["Wild_Route14"] + 7, None, event=True, type="Wild Encounter"), - LocationData("Route 14", "Wild Pokemon - 5", ["Oddish", "Bellsprout"], rom_addresses["Wild_Route14"] + 9, None, + LocationData("Route 14 Grass", "Wild Pokemon - 5", ["Oddish", "Bellsprout"], rom_addresses["Wild_Route14"] + 9, None, event=True, type="Wild Encounter"), - LocationData("Route 14", "Wild Pokemon - 6", "Venonat", rom_addresses["Wild_Route14"] + 11, None, event=True, + LocationData("Route 14 Grass", "Wild Pokemon - 6", "Venonat", rom_addresses["Wild_Route14"] + 11, None, event=True, type="Wild Encounter"), - LocationData("Route 14", "Wild Pokemon - 7", ["Oddish", "Bellsprout"], rom_addresses["Wild_Route14"] + 13, None, + LocationData("Route 14 Grass", "Wild Pokemon - 7", ["Oddish", "Bellsprout"], rom_addresses["Wild_Route14"] + 13, None, event=True, type="Wild Encounter"), - LocationData("Route 14", "Wild Pokemon - 8", ["Gloom", "Weepinbell"], rom_addresses["Wild_Route14"] + 15, None, + LocationData("Route 14 Grass", "Wild Pokemon - 8", ["Gloom", "Weepinbell"], rom_addresses["Wild_Route14"] + 15, None, event=True, type="Wild Encounter"), - LocationData("Route 14", "Wild Pokemon - 9", "Pidgeotto", rom_addresses["Wild_Route14"] + 17, None, event=True, + LocationData("Route 14 Grass", "Wild Pokemon - 9", "Pidgeotto", rom_addresses["Wild_Route14"] + 17, None, event=True, type="Wild Encounter"), - LocationData("Route 14", "Wild Pokemon - 10", "Pidgeotto", rom_addresses["Wild_Route14"] + 19, None, event=True, + LocationData("Route 14 Grass", "Wild Pokemon - 10", "Pidgeotto", rom_addresses["Wild_Route14"] + 19, None, event=True, type="Wild Encounter"), LocationData("Route 15", "Wild Pokemon - 1", ["Oddish", "Bellsprout"], rom_addresses["Wild_Route15"] + 1, None, event=True, type="Wild Encounter"), @@ -1653,9 +1994,9 @@ def __init__(self, flag): LocationData("Route 16", "Sleeping Pokemon", "Snorlax", rom_addresses["Static_Encounter_Snorlax_B"], None, event=True, type="Missable Pokemon"), - LocationData("Saffron City", "Fighting Dojo Gift 1", "Hitmonlee", rom_addresses["Gift_Hitmonlee"], + LocationData("Fighting Dojo", "Gift 1", "Hitmonlee", rom_addresses["Gift_Hitmonlee"], None, event=True, type="Missable Pokemon"), - LocationData("Saffron City", "Fighting Dojo Gift 2", "Hitmonchan", rom_addresses["Gift_Hitmonchan"], + LocationData("Fighting Dojo", "Gift 2", "Hitmonchan", rom_addresses["Gift_Hitmonchan"], None, event=True, type="Missable Pokemon"), LocationData("Pallet Town", "Starter 1", "Charmander", [rom_addresses["Starter1_A"], @@ -1697,6 +2038,7 @@ def __init__(self, flag): None, event=True, type="Legendary Pokemon"), LocationData("Vermilion City", "Legendary Pokemon", "Mew", rom_addresses["Static_Encounter_Mew"], None, event=True, type="Legendary Pokemon"), + ] for i, location in enumerate(location_data): diff --git a/worlds/pokemon_rb/logic.py b/worlds/pokemon_rb/logic.py index 8b60bd8b58d6..6f2c1d795e70 100644 --- a/worlds/pokemon_rb/logic.py +++ b/worlds/pokemon_rb/logic.py @@ -59,6 +59,15 @@ def pokemon_rb_has_badges(self, count, player): return len([item for item in ["Boulder Badge", "Cascade Badge", "Thunder Badge", "Rainbow Badge", "Marsh Badge", "Soul Badge", "Volcano Badge", "Earth Badge"] if self.has(item, player)]) >= count + def pokemon_rb_oaks_aide(self, count, player): + if self.multiworld.randomize_pokedex[player].value > 0: + if not self.has("Pokedex", player): + return False + else: + if not self.has("Oak's Parcel", player): + return False + return self.pokemon_rb_has_pokemon(count, player) + def pokemon_rb_has_pokemon(self, count, player): obtained_pokemon = set() for pokemon in poke_data.pokemon_data.keys(): diff --git a/worlds/pokemon_rb/options.py b/worlds/pokemon_rb/options.py index 6cb4267a9996..3b6739a99dde 100644 --- a/worlds/pokemon_rb/options.py +++ b/worlds/pokemon_rb/options.py @@ -1,5 +1,5 @@ -from Options import Toggle, Choice, Range, SpecialRange, FreeText, TextChoice +from Options import Toggle, Choice, Range, SpecialRange, TextChoice, DeathLink class GameVersion(Choice): @@ -10,23 +10,25 @@ class GameVersion(Choice): default = "random" -class TrainerName(FreeText): - """Your trainer name. Cannot exceed 7 characters. - See the setup guide on archipelago.gg for a list of allowed characters.""" +class TrainerName(TextChoice): + """Your trainer name. If not set to choose_in_game, must be a name not exceeding 7 characters, and the prompt to + name your character in-game will be skipped. See the setup guide on archipelago.gg for a list of allowed characters.""" display_name = "Trainer Name" - default = "ASH" + option_choose_in_game = -1 + default = -1 -class RivalName(FreeText): - """Your rival's name. Cannot exceed 7 characters. - See the setup guide on archipelago.gg for a list of allowed characters.""" +class RivalName(TextChoice): + """Your rival's name. If not set to choose_in_game, must be a name not exceeding 7 characters, and the prompt to + name your rival in-game will be skipped. See the setup guide on archipelago.gg for a list of allowed characters.""" display_name = "Rival's Name" - default = "GARY" + option_choose_in_game = -1 + default = -1 class Goal(Choice): - """If Professor Oak is selected, your victory condition will require challenging and defeating Oak after becoming""" - """Champion and defeating or capturing the Pokemon at the end of Cerulean Cave.""" + """If Professor Oak is selected, your victory condition will require challenging and defeating Oak after becoming + Champion and defeating or capturing the Pokemon at the end of Cerulean Cave.""" display_name = "Goal" option_pokemon_league = 0 option_professor_oak = 1 @@ -59,8 +61,8 @@ class ViridianGymCondition(Range): class CeruleanCaveCondition(Range): - """Number of badges, HMs, and key items (not counting items you can lose) required to access Cerulean Cave.""" - """If extra_key_items is turned on, the number chosen will be increased by 4.""" + """Number of badges, HMs, and key items (not counting items you can lose) required to access Cerulean Cave. + If extra_key_items is turned on, the number chosen will be increased by 4.""" display_name = "Cerulean Cave Condition" range_start = 0 range_end = 25 @@ -97,8 +99,8 @@ class BadgesNeededForHMMoves(Choice): class OldMan(Choice): - """With Open Viridian City, the Old Man will let you through without needing to turn in Oak's Parcel.""" - """Early Parcel will ensure Oak's Parcel is available at the beginning of your game.""" + """With Open Viridian City, the Old Man will let you through without needing to turn in Oak's Parcel. + Early Parcel will ensure Oak's Parcel is available at the beginning of your game.""" display_name = "Old Man" option_vanilla = 0 option_early_parcel = 1 @@ -106,6 +108,15 @@ class OldMan(Choice): default = 1 +class RandomizePokedex(Choice): + """Randomize the location of the Pokedex, or start with it. It is required to receive items from Oak's Aides.""" + display_name = "Randomize Pokedex" + option_vanilla = 0 + option_randomize = 1 + option_start_with = 2 + default = 0 + + class Tea(Toggle): """Adds a Tea item to the item pool which the Saffron guards require instead of the vending machine drinks. Adds a location check to the Celadon Mansion 1F, where Tea is acquired in FireRed and LeafGreen.""" @@ -144,6 +155,14 @@ class RandomizeHiddenItems(Choice): default = 0 +class TrainerSanity(Toggle): + """Add a location check to every trainer in the game, which can be obtained by talking to a trainer after defeating + them. Does not affect gym leaders and some scripted event battles (including all Rival, Giovanni, and + Cinnabar Gym battles).""" + display_name = "Trainersanity" + default = 0 + + class FreeFlyLocation(Toggle): """One random fly destination will be unlocked by default.""" display_name = "Free Fly Location" @@ -180,7 +199,7 @@ class OaksAidRt15(Range): class ExpModifier(SpecialRange): """Modifier for EXP gained. When specifying a number, exp is multiplied by this amount and divided by 16.""" display_name = "Exp Modifier" - range_start = 0 + range_start = 1 range_end = 255 default = 16 special_range_names = { @@ -330,6 +349,13 @@ class StartWithFourMoves(Toggle): default = 0 +class SameTypeAttackBonus(Toggle): + """Here you can disable Same Type Attack Bonus, so that a move matching a Pokemon's type has no benefit. + If disabled, all moves will gain 25% extra damage, instead of same type moves gaining 50% extra damage.""" + display_name = "Same Type Attack Bonus" + default = 1 + + class TMCompatibility(Choice): """Randomize which Pokemon can learn each TM. prefer_types: 90% chance if Pokemon's type matches the move, 50% chance if move is Normal type and the Pokemon is not, and 25% chance otherwise. Pokemon will retain the same @@ -379,31 +405,54 @@ class SecondaryTypeChance(SpecialRange): } -class RandomizeTypeChartTypes(Choice): - """The game's type chart consists of 3 columns: attacking type, defending type, and type effectiveness. - Matchups that have regular type effectiveness are not in the chart. Shuffle will shuffle the attacking types - across the attacking type column and the defending types across the defending type column (so for example Normal - type will still have exactly 2 types that it receives non-regular damage from, and 2 types it deals non-regular - damage to). Randomize will randomize each type in both columns to any random type.""" - display_name = "Randomize Type Chart Types" +class RandomizeTypeChart(Choice): + """Randomize the type chart. If 'randomize' is chosen, the matchup weight options will determine the weights. + If the numbers chosen across the 4 settings add up to exactly 225, they will be the exact numbers of those matchups. + Otherwise, the normal super effective, and not very effective matchup settings will be used as weights. + The immunities option will always be the exact amount of immunity matchups. + If 'chaos' is chosen, the matchup settings will be ignored and every type matchup will be given a random damage + modifier anywhere between 0 to 200% damage, in 10% increments.""" + display_name = "Randomize Type Chart" option_vanilla = 0 - option_shuffle = 1 - option_randomize = 2 + option_randomize = 1 + option_chaos = 2 default = 0 -class RandomizeTypeChartTypeEffectiveness(Choice): - """The game's type chart consists of 3 columns: attacking type, defending type, and type effectiveness. - Matchups that have regular type effectiveness are not in the chart. Shuffle will shuffle the type effectiveness - across the type effectiveness column (so for example there will always be 6 type immunities). Randomize will - randomize each entry in the table to no effect, not very effective, or super effective; with no effect occurring - at a low chance. Chaos will randomize the values to anywhere between 0% and 200% damage, in 10% increments.""" - display_name = "Randomize Type Chart Type Effectiveness" - option_vanilla = 0 - option_shuffle = 1 - option_randomize = 2 - option_chaos = 3 - default = 0 +class NormalMatchups(Range): + """If 'randomize' is chosen for randomize_type_chart, this will be the weight for neutral matchups. + No effect if 'chaos' is chosen""" + display_name = "Normal Matchups" + default = 143 + range_start = 0 + range_end = 225 + + +class SuperEffectiveMatchups(Range): + """If 'randomize' is chosen for randomize_type_chart, this will be the weight for super effective matchups. + No effect if 'chaos' is chosen""" + display_name = "Super Effective Matchups" + default = 38 + range_start = 0 + range_end = 225 + + +class NotVeryEffectiveMatchups(Range): + """If 'randomize' is chosen for randomize_type_chart, this will be the weight for not very effective matchups. + No effect if 'chaos' is chosen""" + display_name = "Not Very Effective Matchups" + default = 38 + range_start = 0 + range_end = 225 + + +class ImmunityMatchups(Range): + """If 'randomize' is chosen for randomize_type_chart, this will be the exact number of immunities. + No effect if 'chaos' is chosen""" + display_name = "Immunity Matchups" + default = 6 + range_start = 0 + range_end = 100 class SafariZoneNormalBattles(Toggle): @@ -425,6 +474,23 @@ class ReusableTMs(Toggle): default = 0 +class BetterShops(Choice): + """Change every town's Pokemart to contain all normal Pokemart items. Additionally, you can add the Master Ball + to these shops.""" + display_name = "Better Shops" + option_off = 0 + option_on = 1 + option_add_master_ball = 2 + default = 0 + + +class MasterBallPrice(Range): + """Price for Master Balls. Can only be bought if better_shops is set to add_master_ball, but this will affect the + sell price regardless. Vanilla is 0""" + range_end = 999999 + default = 5000 + + class StartingMoney(Range): """The amount of money you start with.""" display_name = "Starting Money" @@ -433,6 +499,21 @@ class StartingMoney(Range): range_end = 999999 +class LoseMoneyOnBlackout(Toggle): + """Lose half your money when blacking out, as in vanilla.""" + display_name = "Lose Money on Blackout" + default = 1 + + +class TrapPercentage(Range): + """Chance for each filler item to be replaced with trap items: Poison Trap, Paralyze Trap, Ice Trap, and + Fire Trap. These traps apply the status to your entire party! Keep in mind that trainersanity vastly increases the + number of filler items. Make sure to stock up on Ice Heals!""" + display_name = "Trap Percentage" + range_end = 100 + default = 0 + + pokemon_rb_options = { "game_version": GameVersion, "trainer_name": TrainerName, @@ -445,11 +526,13 @@ class StartingMoney(Range): "second_fossil_check_condition": SecondFossilCheckCondition, "badgesanity": BadgeSanity, "old_man": OldMan, + "randomize_pokedex": RandomizePokedex, "tea": Tea, "extra_key_items": ExtraKeyItems, "extra_strength_boulders": ExtraStrengthBoulders, "require_item_finder": RequireItemFinder, "randomize_hidden_items": RandomizeHiddenItems, + "trainersanity": TrainerSanity, "badges_needed_for_hm_moves": BadgesNeededForHMMoves, "free_fly_location": FreeFlyLocation, "oaks_aide_rt_2": OaksAidRt2, @@ -470,14 +553,23 @@ class StartingMoney(Range): "trainer_legendaries": TrainerLegendaries, "randomize_pokemon_movesets": RandomizePokemonMovesets, "start_with_four_moves": StartWithFourMoves, + "same_type_attack_bonus": SameTypeAttackBonus, "tm_compatibility": TMCompatibility, "hm_compatibility": HMCompatibility, "randomize_pokemon_types": RandomizePokemonTypes, "secondary_type_chance": SecondaryTypeChance, - "randomize_type_matchup_types": RandomizeTypeChartTypes, - "randomize_type_matchup_type_effectiveness": RandomizeTypeChartTypeEffectiveness, + "randomize_type_chart": RandomizeTypeChart, + "normal_matchups": NormalMatchups, + "super_effective_matchups": SuperEffectiveMatchups, + "not_very_effective_matchups": NotVeryEffectiveMatchups, + "immunity_matchups": ImmunityMatchups, "safari_zone_normal_battles": SafariZoneNormalBattles, "normalize_encounter_chances": NormalizeEncounterChances, "reusable_tms": ReusableTMs, + "better_shops": BetterShops, + "master_ball_price": MasterBallPrice, "starting_money": StartingMoney, + "lose_money_on_blackout": LoseMoneyOnBlackout, + "trap_percentage": TrapPercentage, + "death_link": DeathLink } \ No newline at end of file diff --git a/worlds/pokemon_rb/regions.py b/worlds/pokemon_rb/regions.py index 9640e0a82370..904dc3a127ac 100644 --- a/worlds/pokemon_rb/regions.py +++ b/worlds/pokemon_rb/regions.py @@ -1,19 +1,15 @@ from BaseClasses import MultiWorld, Region, Entrance, RegionType, LocationProgressType -from worlds.generic.Rules import add_item_rule from .locations import location_data, PokemonRBLocation def create_region(world: MultiWorld, player: int, name: str, locations_per_region=None, exits=None): ret = Region(name, RegionType.Generic, name, player, world) for location in locations_per_region.get(name, []): - if (world.randomize_hidden_items[player].value or "Hidden" not in location.name) and \ - (world.extra_key_items[player].value or name != "Rock Tunnel B1F" or "Item" not in location.name) and \ - (world.tea[player].value or location.name != "Celadon City - Mansion Lady"): - location.parent_region = ret - ret.locations.append(location) - if world.randomize_hidden_items[player].value == 2 and "Hidden" in location.name: - location.progress_type = LocationProgressType.EXCLUDED + location.parent_region = ret + ret.locations.append(location) + if world.randomize_hidden_items[player] == "exclude" and "Hidden" in location.name: + location.progress_type = LocationProgressType.EXCLUDED if exits: for exit in exits: ret.exits.append(Entrance(player, exit, ret)) @@ -21,265 +17,275 @@ def create_region(world: MultiWorld, player: int, name: str, locations_per_regio return ret -def create_regions(world: MultiWorld, player: int): +def create_regions(multiworld: MultiWorld, player: int): locations_per_region = {} for location in location_data: locations_per_region.setdefault(location.region, []) - locations_per_region[location.region].append(PokemonRBLocation(player, location.name, location.address, + if location.inclusion(multiworld, player): + locations_per_region[location.region].append(PokemonRBLocation(player, location.name, location.address, location.rom_address)) regions = [ - create_region(world, player, "Menu", locations_per_region), - create_region(world, player, "Anywhere", locations_per_region), - create_region(world, player, "Fossil", locations_per_region), - create_region(world, player, "Pallet Town", locations_per_region), - create_region(world, player, "Route 1", locations_per_region), - create_region(world, player, "Viridian City", locations_per_region), - create_region(world, player, "Viridian City North", locations_per_region), - create_region(world, player, "Viridian Gym", locations_per_region), - create_region(world, player, "Route 2", locations_per_region), - create_region(world, player, "Route 2 East", locations_per_region), - create_region(world, player, "Diglett's Cave", locations_per_region), - create_region(world, player, "Route 22", locations_per_region), - create_region(world, player, "Route 23", locations_per_region), - create_region(world, player, "Viridian Forest", locations_per_region), - create_region(world, player, "Pewter City", locations_per_region), - create_region(world, player, "Pewter Gym", locations_per_region), - create_region(world, player, "Route 3", locations_per_region), - create_region(world, player, "Mt Moon 1F", locations_per_region), - create_region(world, player, "Mt Moon B1F", locations_per_region), - create_region(world, player, "Mt Moon B2F", locations_per_region), - create_region(world, player, "Route 4", locations_per_region), - create_region(world, player, "Cerulean City", locations_per_region), - create_region(world, player, "Cerulean Gym", locations_per_region), - create_region(world, player, "Route 24", locations_per_region), - create_region(world, player, "Route 25", locations_per_region), - create_region(world, player, "Route 9", locations_per_region), - create_region(world, player, "Route 10 North", locations_per_region), - create_region(world, player, "Rock Tunnel 1F", locations_per_region), - create_region(world, player, "Rock Tunnel B1F", locations_per_region), - create_region(world, player, "Power Plant", locations_per_region), - create_region(world, player, "Route 10 South", locations_per_region), - create_region(world, player, "Lavender Town", locations_per_region), - create_region(world, player, "Pokemon Tower 1F", locations_per_region), - create_region(world, player, "Pokemon Tower 2F", locations_per_region), - create_region(world, player, "Pokemon Tower 3F", locations_per_region), - create_region(world, player, "Pokemon Tower 4F", locations_per_region), - create_region(world, player, "Pokemon Tower 5F", locations_per_region), - create_region(world, player, "Pokemon Tower 6F", locations_per_region), - create_region(world, player, "Pokemon Tower 7F", locations_per_region), - create_region(world, player, "Route 5", locations_per_region), - create_region(world, player, "Saffron City", locations_per_region), - create_region(world, player, "Saffron Gym", locations_per_region), - create_region(world, player, "Copycat's House", locations_per_region), - create_region(world, player, "Underground Tunnel North-South", locations_per_region), - create_region(world, player, "Route 6", locations_per_region), - create_region(world, player, "Vermilion City", locations_per_region), - create_region(world, player, "Vermilion Gym", locations_per_region), - create_region(world, player, "S.S. Anne 1F", locations_per_region), - create_region(world, player, "S.S. Anne B1F", locations_per_region), - create_region(world, player, "S.S. Anne 2F", locations_per_region), - create_region(world, player, "Route 11", locations_per_region), - create_region(world, player, "Route 11 East", locations_per_region), - create_region(world, player, "Route 12 North", locations_per_region), - create_region(world, player, "Route 12 South", locations_per_region), - create_region(world, player, "Route 12 Grass", locations_per_region), - create_region(world, player, "Route 12 West", locations_per_region), - create_region(world, player, "Route 7", locations_per_region), - create_region(world, player, "Underground Tunnel West-East", locations_per_region), - create_region(world, player, "Route 8", locations_per_region), - create_region(world, player, "Route 8 Grass", locations_per_region), - create_region(world, player, "Celadon City", locations_per_region), - create_region(world, player, "Celadon Prize Corner", locations_per_region), - create_region(world, player, "Celadon Gym", locations_per_region), - create_region(world, player, "Route 16", locations_per_region), - create_region(world, player, "Route 16 North", locations_per_region), - create_region(world, player, "Route 17", locations_per_region), - create_region(world, player, "Route 18", locations_per_region), - create_region(world, player, "Fuchsia City", locations_per_region), - create_region(world, player, "Fuchsia Gym", locations_per_region), - create_region(world, player, "Safari Zone Gate", locations_per_region), - create_region(world, player, "Safari Zone Center", locations_per_region), - create_region(world, player, "Safari Zone East", locations_per_region), - create_region(world, player, "Safari Zone North", locations_per_region), - create_region(world, player, "Safari Zone West", locations_per_region), - create_region(world, player, "Route 15", locations_per_region), - create_region(world, player, "Route 14", locations_per_region), - create_region(world, player, "Route 13", locations_per_region), - create_region(world, player, "Route 19", locations_per_region), - create_region(world, player, "Route 20 East", locations_per_region), - create_region(world, player, "Route 20 West", locations_per_region), - create_region(world, player, "Seafoam Islands 1F", locations_per_region), - create_region(world, player, "Seafoam Islands B1F", locations_per_region), - create_region(world, player, "Seafoam Islands B2F", locations_per_region), - create_region(world, player, "Seafoam Islands B3F", locations_per_region), - create_region(world, player, "Seafoam Islands B4F", locations_per_region), - create_region(world, player, "Cinnabar Island", locations_per_region), - create_region(world, player, "Cinnabar Gym", locations_per_region), - create_region(world, player, "Route 21", locations_per_region), - create_region(world, player, "Silph Co 1F", locations_per_region), - create_region(world, player, "Silph Co 2F", locations_per_region), - create_region(world, player, "Silph Co 3F", locations_per_region), - create_region(world, player, "Silph Co 4F", locations_per_region), - create_region(world, player, "Silph Co 5F", locations_per_region), - create_region(world, player, "Silph Co 6F", locations_per_region), - create_region(world, player, "Silph Co 7F", locations_per_region), - create_region(world, player, "Silph Co 8F", locations_per_region), - create_region(world, player, "Silph Co 9F", locations_per_region), - create_region(world, player, "Silph Co 10F", locations_per_region), - create_region(world, player, "Silph Co 11F", locations_per_region), - create_region(world, player, "Rocket Hideout B1F", locations_per_region), - create_region(world, player, "Rocket Hideout B2F", locations_per_region), - create_region(world, player, "Rocket Hideout B3F", locations_per_region), - create_region(world, player, "Rocket Hideout B4F", locations_per_region), - create_region(world, player, "Pokemon Mansion 1F", locations_per_region), - create_region(world, player, "Pokemon Mansion 2F", locations_per_region), - create_region(world, player, "Pokemon Mansion 3F", locations_per_region), - create_region(world, player, "Pokemon Mansion B1F", locations_per_region), - create_region(world, player, "Victory Road 1F", locations_per_region), - create_region(world, player, "Victory Road 2F", locations_per_region), - create_region(world, player, "Victory Road 3F", locations_per_region), - create_region(world, player, "Indigo Plateau", locations_per_region), - create_region(world, player, "Cerulean Cave 1F", locations_per_region), - create_region(world, player, "Cerulean Cave 2F", locations_per_region), - create_region(world, player, "Cerulean Cave B1F", locations_per_region), - create_region(world, player, "Evolution", locations_per_region), + create_region(multiworld, player, "Menu", locations_per_region), + create_region(multiworld, player, "Anywhere", locations_per_region), + create_region(multiworld, player, "Fossil", locations_per_region), + create_region(multiworld, player, "Pallet Town", locations_per_region), + create_region(multiworld, player, "Route 1", locations_per_region), + create_region(multiworld, player, "Viridian City", locations_per_region), + create_region(multiworld, player, "Viridian City North", locations_per_region), + create_region(multiworld, player, "Viridian Gym", locations_per_region), + create_region(multiworld, player, "Route 2", locations_per_region), + create_region(multiworld, player, "Route 2 East", locations_per_region), + create_region(multiworld, player, "Diglett's Cave", locations_per_region), + create_region(multiworld, player, "Route 22", locations_per_region), + create_region(multiworld, player, "Route 23", locations_per_region), + create_region(multiworld, player, "Viridian Forest", locations_per_region), + create_region(multiworld, player, "Pewter City", locations_per_region), + create_region(multiworld, player, "Pewter Gym", locations_per_region), + create_region(multiworld, player, "Route 3", locations_per_region), + create_region(multiworld, player, "Mt Moon 1F", locations_per_region), + create_region(multiworld, player, "Mt Moon B1F", locations_per_region), + create_region(multiworld, player, "Mt Moon B2F", locations_per_region), + create_region(multiworld, player, "Route 4", locations_per_region), + create_region(multiworld, player, "Cerulean City", locations_per_region), + create_region(multiworld, player, "Cerulean Gym", locations_per_region), + create_region(multiworld, player, "Route 24", locations_per_region), + create_region(multiworld, player, "Route 25", locations_per_region), + create_region(multiworld, player, "Route 9", locations_per_region), + create_region(multiworld, player, "Route 10 North", locations_per_region), + create_region(multiworld, player, "Rock Tunnel 1F", locations_per_region), + create_region(multiworld, player, "Rock Tunnel B1F", locations_per_region), + create_region(multiworld, player, "Power Plant", locations_per_region), + create_region(multiworld, player, "Route 10 South", locations_per_region), + create_region(multiworld, player, "Lavender Town", locations_per_region), + create_region(multiworld, player, "Pokemon Tower 1F", locations_per_region), + create_region(multiworld, player, "Pokemon Tower 2F", locations_per_region), + create_region(multiworld, player, "Pokemon Tower 3F", locations_per_region), + create_region(multiworld, player, "Pokemon Tower 4F", locations_per_region), + create_region(multiworld, player, "Pokemon Tower 5F", locations_per_region), + create_region(multiworld, player, "Pokemon Tower 6F", locations_per_region), + create_region(multiworld, player, "Pokemon Tower 7F", locations_per_region), + create_region(multiworld, player, "Route 5", locations_per_region), + create_region(multiworld, player, "Saffron City", locations_per_region), + create_region(multiworld, player, "Fighting Dojo", locations_per_region), + create_region(multiworld, player, "Saffron Gym", locations_per_region), + create_region(multiworld, player, "Copycat's House", locations_per_region), + create_region(multiworld, player, "Underground Tunnel North-South", locations_per_region), + create_region(multiworld, player, "Route 6", locations_per_region), + create_region(multiworld, player, "Vermilion City", locations_per_region), + create_region(multiworld, player, "Vermilion Gym", locations_per_region), + create_region(multiworld, player, "S.S. Anne 1F", locations_per_region), + create_region(multiworld, player, "S.S. Anne B1F", locations_per_region), + create_region(multiworld, player, "S.S. Anne 2F", locations_per_region), + create_region(multiworld, player, "S.S. Anne 3F", locations_per_region), + create_region(multiworld, player, "Route 11", locations_per_region), + create_region(multiworld, player, "Route 11 East", locations_per_region), + create_region(multiworld, player, "Route 12 North", locations_per_region), + create_region(multiworld, player, "Route 12 South", locations_per_region), + create_region(multiworld, player, "Route 12 Grass", locations_per_region), + create_region(multiworld, player, "Route 12 West", locations_per_region), + create_region(multiworld, player, "Route 7", locations_per_region), + create_region(multiworld, player, "Underground Tunnel West-East", locations_per_region), + create_region(multiworld, player, "Route 8", locations_per_region), + create_region(multiworld, player, "Route 8 Grass", locations_per_region), + create_region(multiworld, player, "Celadon City", locations_per_region), + create_region(multiworld, player, "Celadon Prize Corner", locations_per_region), + create_region(multiworld, player, "Celadon Gym", locations_per_region), + create_region(multiworld, player, "Route 16", locations_per_region), + create_region(multiworld, player, "Route 16 West", locations_per_region), + create_region(multiworld, player, "Route 16 North", locations_per_region), + create_region(multiworld, player, "Route 17", locations_per_region), + create_region(multiworld, player, "Route 18", locations_per_region), + create_region(multiworld, player, "Fuchsia City", locations_per_region), + create_region(multiworld, player, "Fuchsia Gym", locations_per_region), + create_region(multiworld, player, "Safari Zone Gate", locations_per_region), + create_region(multiworld, player, "Safari Zone Center", locations_per_region), + create_region(multiworld, player, "Safari Zone East", locations_per_region), + create_region(multiworld, player, "Safari Zone North", locations_per_region), + create_region(multiworld, player, "Safari Zone West", locations_per_region), + create_region(multiworld, player, "Route 15", locations_per_region), + create_region(multiworld, player, "Route 14", locations_per_region), + create_region(multiworld, player, "Route 14 Grass", locations_per_region), + create_region(multiworld, player, "Route 13", locations_per_region), + create_region(multiworld, player, "Route 13 East", locations_per_region), + create_region(multiworld, player, "Route 19", locations_per_region), + create_region(multiworld, player, "Route 20 East", locations_per_region), + create_region(multiworld, player, "Route 20 West", locations_per_region), + create_region(multiworld, player, "Seafoam Islands 1F", locations_per_region), + create_region(multiworld, player, "Seafoam Islands B1F", locations_per_region), + create_region(multiworld, player, "Seafoam Islands B2F", locations_per_region), + create_region(multiworld, player, "Seafoam Islands B3F", locations_per_region), + create_region(multiworld, player, "Seafoam Islands B4F", locations_per_region), + create_region(multiworld, player, "Cinnabar Island", locations_per_region), + create_region(multiworld, player, "Cinnabar Gym", locations_per_region), + create_region(multiworld, player, "Route 21", locations_per_region), + create_region(multiworld, player, "Silph Co 1F", locations_per_region), + create_region(multiworld, player, "Silph Co 2F", locations_per_region), + create_region(multiworld, player, "Silph Co 3F", locations_per_region), + create_region(multiworld, player, "Silph Co 4F", locations_per_region), + create_region(multiworld, player, "Silph Co 5F", locations_per_region), + create_region(multiworld, player, "Silph Co 6F", locations_per_region), + create_region(multiworld, player, "Silph Co 7F", locations_per_region), + create_region(multiworld, player, "Silph Co 8F", locations_per_region), + create_region(multiworld, player, "Silph Co 9F", locations_per_region), + create_region(multiworld, player, "Silph Co 10F", locations_per_region), + create_region(multiworld, player, "Silph Co 11F", locations_per_region), + create_region(multiworld, player, "Rocket Hideout B1F", locations_per_region), + create_region(multiworld, player, "Rocket Hideout B2F", locations_per_region), + create_region(multiworld, player, "Rocket Hideout B3F", locations_per_region), + create_region(multiworld, player, "Rocket Hideout B4F", locations_per_region), + create_region(multiworld, player, "Pokemon Mansion 1F", locations_per_region), + create_region(multiworld, player, "Pokemon Mansion 2F", locations_per_region), + create_region(multiworld, player, "Pokemon Mansion 3F", locations_per_region), + create_region(multiworld, player, "Pokemon Mansion B1F", locations_per_region), + create_region(multiworld, player, "Victory Road 1F", locations_per_region), + create_region(multiworld, player, "Victory Road 2F", locations_per_region), + create_region(multiworld, player, "Victory Road 3F", locations_per_region), + create_region(multiworld, player, "Indigo Plateau", locations_per_region), + create_region(multiworld, player, "Cerulean Cave 1F", locations_per_region), + create_region(multiworld, player, "Cerulean Cave 2F", locations_per_region), + create_region(multiworld, player, "Cerulean Cave B1F", locations_per_region), ] - world.regions += regions - connect(world, player, "Menu", "Anywhere", one_way=True) - connect(world, player, "Menu", "Pallet Town", one_way=True) - connect(world, player, "Menu", "Fossil", lambda state: state.pokemon_rb_fossil_checks( + multiworld.regions += regions + connect(multiworld, player, "Menu", "Anywhere", one_way=True) + connect(multiworld, player, "Menu", "Pallet Town", one_way=True) + connect(multiworld, player, "Menu", "Fossil", lambda state: state.pokemon_rb_fossil_checks( state.multiworld.second_fossil_check_condition[player].value, player), one_way=True) - connect(world, player, "Pallet Town", "Route 1") - connect(world, player, "Route 1", "Viridian City") - connect(world, player, "Viridian City", "Route 22") - connect(world, player, "Route 22", "Route 23", + connect(multiworld, player, "Pallet Town", "Route 1") + connect(multiworld, player, "Route 1", "Viridian City") + connect(multiworld, player, "Viridian City", "Route 22") + connect(multiworld, player, "Route 22", "Route 23", lambda state: state.pokemon_rb_has_badges(state.multiworld.victory_road_condition[player].value, player) and state.pokemon_rb_can_surf(player)) - connect(world, player, "Viridian City North", "Viridian Gym", lambda state: + connect(multiworld, player, "Viridian City North", "Viridian Gym", lambda state: state.pokemon_rb_has_badges(state.multiworld.viridian_gym_condition[player].value, player), one_way=True) - connect(world, player, "Route 2", "Route 2 East", lambda state: state.pokemon_rb_can_cut(player)) - connect(world, player, "Route 2 East", "Diglett's Cave", lambda state: state.pokemon_rb_can_cut(player)) - connect(world, player, "Route 2", "Viridian City North") - connect(world, player, "Route 2", "Viridian Forest") - connect(world, player, "Route 2", "Pewter City") - connect(world, player, "Pewter City", "Pewter Gym", one_way=True) - connect(world, player, "Pewter City", "Route 3") - connect(world, player, "Route 4", "Route 3", one_way=True) - connect(world, player, "Mt Moon 1F", "Mt Moon B1F", one_way=True) - connect(world, player, "Mt Moon B1F", "Mt Moon B2F", one_way=True) - connect(world, player, "Mt Moon B1F", "Route 4", one_way=True) - connect(world, player, "Route 4", "Cerulean City") - connect(world, player, "Cerulean City", "Cerulean Gym", one_way=True) - connect(world, player, "Cerulean City", "Route 24", one_way=True) - connect(world, player, "Route 24", "Route 25", one_way=True) - connect(world, player, "Cerulean City", "Route 9", lambda state: state.pokemon_rb_can_cut(player)) - connect(world, player, "Route 9", "Route 10 North") - connect(world, player, "Route 10 North", "Rock Tunnel 1F", lambda state: state.pokemon_rb_can_flash(player)) - connect(world, player, "Route 10 North", "Power Plant", lambda state: state.pokemon_rb_can_surf(player) and - (state.has("Plant Key", player) or not state.multiworld.extra_key_items[player].value), one_way=True) - connect(world, player, "Rock Tunnel 1F", "Route 10 South", lambda state: state.pokemon_rb_can_flash(player)) - connect(world, player, "Rock Tunnel 1F", "Rock Tunnel B1F") - connect(world, player, "Lavender Town", "Pokemon Tower 1F", one_way=True) - connect(world, player, "Lavender Town", "Pokemon Tower 1F", one_way=True) - connect(world, player, "Pokemon Tower 1F", "Pokemon Tower 2F", one_way=True) - connect(world, player, "Pokemon Tower 2F", "Pokemon Tower 3F", one_way=True) - connect(world, player, "Pokemon Tower 3F", "Pokemon Tower 4F", one_way=True) - connect(world, player, "Pokemon Tower 4F", "Pokemon Tower 5F", one_way=True) - connect(world, player, "Pokemon Tower 5F", "Pokemon Tower 6F", one_way=True) - connect(world, player, "Pokemon Tower 6F", "Pokemon Tower 7F", lambda state: state.has("Silph Scope", player)) - connect(world, player, "Cerulean City", "Route 5") - connect(world, player, "Route 5", "Saffron City", lambda state: state.pokemon_rb_can_pass_guards(player)) - connect(world, player, "Route 5", "Underground Tunnel North-South") - connect(world, player, "Route 6", "Underground Tunnel North-South") - connect(world, player, "Route 6", "Saffron City", lambda state: state.pokemon_rb_can_pass_guards(player)) - connect(world, player, "Route 7", "Saffron City", lambda state: state.pokemon_rb_can_pass_guards(player)) - connect(world, player, "Route 8", "Saffron City", lambda state: state.pokemon_rb_can_pass_guards(player)) - connect(world, player, "Saffron City", "Copycat's House", lambda state: state.has("Silph Co Liberated", player), one_way=True) - connect(world, player, "Saffron City", "Saffron Gym", lambda state: state.has("Silph Co Liberated", player), one_way=True) - connect(world, player, "Route 6", "Vermilion City") - connect(world, player, "Vermilion City", "Vermilion Gym", lambda state: state.pokemon_rb_can_surf(player) or state.pokemon_rb_can_cut(player), one_way=True) - connect(world, player, "Vermilion City", "S.S. Anne 1F", lambda state: state.has("S.S. Ticket", player), one_way=True) - connect(world, player, "S.S. Anne 1F", "S.S. Anne 2F", one_way=True) - connect(world, player, "S.S. Anne 1F", "S.S. Anne B1F", one_way=True) - connect(world, player, "Vermilion City", "Route 11") - connect(world, player, "Vermilion City", "Diglett's Cave") - connect(world, player, "Route 12 West", "Route 11 East", lambda state: state.pokemon_rb_can_strength(player) or not state.multiworld.extra_strength_boulders[player].value) - connect(world, player, "Route 12 North", "Route 12 South", lambda state: state.has("Poke Flute", player) or state.pokemon_rb_can_surf( player)) - connect(world, player, "Route 12 West", "Route 12 North", lambda state: state.has("Poke Flute", player)) - connect(world, player, "Route 12 West", "Route 12 South", lambda state: state.has("Poke Flute", player)) - connect(world, player, "Route 12 South", "Route 12 Grass", lambda state: state.pokemon_rb_can_cut(player)) - connect(world, player, "Route 12 North", "Lavender Town") - connect(world, player, "Route 7", "Lavender Town") - connect(world, player, "Route 10 South", "Lavender Town") - connect(world, player, "Route 7", "Underground Tunnel West-East") - connect(world, player, "Route 8", "Underground Tunnel West-East") - connect(world, player, "Route 8", "Celadon City") - connect(world, player, "Route 8", "Route 8 Grass", lambda state: state.pokemon_rb_can_cut(player), one_way=True) - connect(world, player, "Route 7", "Celadon City") - connect(world, player, "Celadon City", "Celadon Gym", lambda state: state.pokemon_rb_can_cut(player), one_way=True) - connect(world, player, "Celadon City", "Celadon Prize Corner") - connect(world, player, "Celadon City", "Route 16") - connect(world, player, "Route 16", "Route 16 North", lambda state: state.pokemon_rb_can_cut(player), one_way=True) - connect(world, player, "Route 16", "Route 17", lambda state: state.has("Poke Flute", player) and state.has("Bicycle", player)) - connect(world, player, "Route 17", "Route 18", lambda state: state.has("Bicycle", player)) - connect(world, player, "Fuchsia City", "Fuchsia Gym", one_way=True) - connect(world, player, "Fuchsia City", "Route 18") - connect(world, player, "Fuchsia City", "Safari Zone Gate", one_way=True) - connect(world, player, "Safari Zone Gate", "Safari Zone Center", lambda state: state.has("Safari Pass", player) or not state.multiworld.extra_key_items[player].value, one_way=True) - connect(world, player, "Safari Zone Center", "Safari Zone East", one_way=True) - connect(world, player, "Safari Zone Center", "Safari Zone West", one_way=True) - connect(world, player, "Safari Zone Center", "Safari Zone North", one_way=True) - connect(world, player, "Fuchsia City", "Route 15") - connect(world, player, "Route 15", "Route 14") - connect(world, player, "Route 14", "Route 13") - connect(world, player, "Route 13", "Route 12 South", lambda state: state.pokemon_rb_can_strength(player) or state.pokemon_rb_can_surf(player) or not state.multiworld.extra_strength_boulders[player].value) - connect(world, player, "Fuchsia City", "Route 19", lambda state: state.pokemon_rb_can_surf(player)) - connect(world, player, "Route 20 East", "Route 19") - connect(world, player, "Route 20 West", "Cinnabar Island", lambda state: state.pokemon_rb_can_surf(player)) - connect(world, player, "Route 20 West", "Seafoam Islands 1F") - connect(world, player, "Route 20 East", "Seafoam Islands 1F", one_way=True) - connect(world, player, "Seafoam Islands 1F", "Route 20 East", lambda state: state.pokemon_rb_can_strength(player), one_way=True) - connect(world, player, "Viridian City", "Viridian City North", lambda state: state.has("Oak's Parcel", player) or state.multiworld.old_man[player].value == 2 or state.pokemon_rb_can_cut(player)) - connect(world, player, "Route 3", "Mt Moon 1F", one_way=True) - connect(world, player, "Route 11", "Route 11 East", lambda state: state.pokemon_rb_can_strength(player)) - connect(world, player, "Cinnabar Island", "Cinnabar Gym", lambda state: state.has("Secret Key", player), one_way=True) - connect(world, player, "Cinnabar Island", "Pokemon Mansion 1F", lambda state: state.has("Mansion Key", player) or not state.multiworld.extra_key_items[player].value, one_way=True) - connect(world, player, "Seafoam Islands 1F", "Seafoam Islands B1F", one_way=True) - connect(world, player, "Seafoam Islands B1F", "Seafoam Islands B2F", one_way=True) - connect(world, player, "Seafoam Islands B2F", "Seafoam Islands B3F", one_way=True) - connect(world, player, "Seafoam Islands B3F", "Seafoam Islands B4F", one_way=True) - connect(world, player, "Route 21", "Cinnabar Island", lambda state: state.pokemon_rb_can_surf(player)) - connect(world, player, "Pallet Town", "Route 21", lambda state: state.pokemon_rb_can_surf(player)) - connect(world, player, "Saffron City", "Silph Co 1F", lambda state: state.has("Fuji Saved", player), one_way=True) - connect(world, player, "Silph Co 1F", "Silph Co 2F", one_way=True) - connect(world, player, "Silph Co 2F", "Silph Co 3F", one_way=True) - connect(world, player, "Silph Co 3F", "Silph Co 4F", one_way=True) - connect(world, player, "Silph Co 4F", "Silph Co 5F", one_way=True) - connect(world, player, "Silph Co 5F", "Silph Co 6F", one_way=True) - connect(world, player, "Silph Co 6F", "Silph Co 7F", one_way=True) - connect(world, player, "Silph Co 7F", "Silph Co 8F", one_way=True) - connect(world, player, "Silph Co 8F", "Silph Co 9F", one_way=True) - connect(world, player, "Silph Co 9F", "Silph Co 10F", one_way=True) - connect(world, player, "Silph Co 10F", "Silph Co 11F", one_way=True) - connect(world, player, "Celadon City", "Rocket Hideout B1F", lambda state: state.has("Hideout Key", player) or not state.multiworld.extra_key_items[player].value, one_way=True) - connect(world, player, "Rocket Hideout B1F", "Rocket Hideout B2F", one_way=True) - connect(world, player, "Rocket Hideout B2F", "Rocket Hideout B3F", one_way=True) - connect(world, player, "Rocket Hideout B3F", "Rocket Hideout B4F", one_way=True) - connect(world, player, "Pokemon Mansion 1F", "Pokemon Mansion 2F", one_way=True) - connect(world, player, "Pokemon Mansion 2F", "Pokemon Mansion 3F", one_way=True) - connect(world, player, "Pokemon Mansion 1F", "Pokemon Mansion B1F", one_way=True) - connect(world, player, "Route 23", "Victory Road 1F", lambda state: state.pokemon_rb_can_strength(player), one_way=True) - connect(world, player, "Victory Road 1F", "Victory Road 2F", one_way=True) - connect(world, player, "Victory Road 2F", "Victory Road 3F", one_way=True) - connect(world, player, "Victory Road 2F", "Indigo Plateau", lambda state: state.pokemon_rb_has_badges(state.multiworld.elite_four_condition[player], player), one_way=True) - connect(world, player, "Cerulean City", "Cerulean Cave 1F", lambda state: + connect(multiworld, player, "Route 2", "Route 2 East", lambda state: state.pokemon_rb_can_cut(player)) + connect(multiworld, player, "Route 2 East", "Diglett's Cave", lambda state: state.pokemon_rb_can_cut(player)) + connect(multiworld, player, "Route 2", "Viridian City North") + connect(multiworld, player, "Route 2", "Viridian Forest") + connect(multiworld, player, "Route 2", "Pewter City") + connect(multiworld, player, "Pewter City", "Pewter Gym", one_way=True) + connect(multiworld, player, "Pewter City", "Route 3") + connect(multiworld, player, "Route 4", "Route 3", one_way=True) + connect(multiworld, player, "Mt Moon 1F", "Mt Moon B1F", one_way=True) + connect(multiworld, player, "Mt Moon B1F", "Mt Moon B2F", one_way=True) + connect(multiworld, player, "Mt Moon B1F", "Route 4", one_way=True) + connect(multiworld, player, "Route 4", "Cerulean City") + connect(multiworld, player, "Cerulean City", "Cerulean Gym", one_way=True) + connect(multiworld, player, "Cerulean City", "Route 24", one_way=True) + connect(multiworld, player, "Route 24", "Route 25", one_way=True) + connect(multiworld, player, "Cerulean City", "Route 9", lambda state: state.pokemon_rb_can_cut(player)) + connect(multiworld, player, "Route 9", "Route 10 North") + connect(multiworld, player, "Route 10 North", "Rock Tunnel 1F", lambda state: state.pokemon_rb_can_flash(player)) + connect(multiworld, player, "Route 10 North", "Power Plant", lambda state: state.pokemon_rb_can_surf(player) and + (state.has("Plant Key", player) or not state.multiworld.extra_key_items[player].value), one_way=True) + connect(multiworld, player, "Rock Tunnel 1F", "Route 10 South", lambda state: state.pokemon_rb_can_flash(player)) + connect(multiworld, player, "Rock Tunnel 1F", "Rock Tunnel B1F") + connect(multiworld, player, "Lavender Town", "Pokemon Tower 1F", one_way=True) + connect(multiworld, player, "Lavender Town", "Pokemon Tower 1F", one_way=True) + connect(multiworld, player, "Pokemon Tower 1F", "Pokemon Tower 2F", one_way=True) + connect(multiworld, player, "Pokemon Tower 2F", "Pokemon Tower 3F", one_way=True) + connect(multiworld, player, "Pokemon Tower 3F", "Pokemon Tower 4F", one_way=True) + connect(multiworld, player, "Pokemon Tower 4F", "Pokemon Tower 5F", one_way=True) + connect(multiworld, player, "Pokemon Tower 5F", "Pokemon Tower 6F", one_way=True) + connect(multiworld, player, "Pokemon Tower 6F", "Pokemon Tower 7F", lambda state: state.has("Silph Scope", player)) + connect(multiworld, player, "Cerulean City", "Route 5") + connect(multiworld, player, "Route 5", "Saffron City", lambda state: state.pokemon_rb_can_pass_guards(player)) + connect(multiworld, player, "Saffron City", "Fighting Dojo", one_way=True) + connect(multiworld, player, "Route 5", "Underground Tunnel North-South") + connect(multiworld, player, "Route 6", "Underground Tunnel North-South") + connect(multiworld, player, "Route 6", "Saffron City", lambda state: state.pokemon_rb_can_pass_guards(player)) + connect(multiworld, player, "Route 7", "Saffron City", lambda state: state.pokemon_rb_can_pass_guards(player)) + connect(multiworld, player, "Route 8", "Saffron City", lambda state: state.pokemon_rb_can_pass_guards(player)) + connect(multiworld, player, "Saffron City", "Copycat's House", lambda state: state.has("Silph Co Liberated", player), one_way=True) + connect(multiworld, player, "Saffron City", "Saffron Gym", lambda state: state.has("Silph Co Liberated", player), one_way=True) + connect(multiworld, player, "Route 6", "Vermilion City") + connect(multiworld, player, "Vermilion City", "Vermilion Gym", lambda state: state.pokemon_rb_can_surf(player) or state.pokemon_rb_can_cut(player), one_way=True) + connect(multiworld, player, "Vermilion City", "S.S. Anne 1F", lambda state: state.has("S.S. Ticket", player), one_way=True) + connect(multiworld, player, "S.S. Anne 1F", "S.S. Anne 2F", one_way=True) + connect(multiworld, player, "S.S. Anne 2F", "S.S. Anne 3F", one_way=True) + connect(multiworld, player, "S.S. Anne 1F", "S.S. Anne B1F", one_way=True) + connect(multiworld, player, "Vermilion City", "Route 11") + connect(multiworld, player, "Vermilion City", "Diglett's Cave") + connect(multiworld, player, "Route 12 West", "Route 11 East", lambda state: state.pokemon_rb_can_strength(player) or not state.multiworld.extra_strength_boulders[player].value) + connect(multiworld, player, "Route 12 North", "Route 12 South", lambda state: state.has("Poke Flute", player) or state.pokemon_rb_can_surf(player)) + connect(multiworld, player, "Route 12 West", "Route 12 North", lambda state: state.has("Poke Flute", player)) + connect(multiworld, player, "Route 12 West", "Route 12 South", lambda state: state.has("Poke Flute", player)) + connect(multiworld, player, "Route 12 South", "Route 12 Grass", lambda state: state.pokemon_rb_can_cut(player)) + connect(multiworld, player, "Route 12 North", "Lavender Town") + connect(multiworld, player, "Route 7", "Lavender Town") + connect(multiworld, player, "Route 10 South", "Lavender Town") + connect(multiworld, player, "Route 7", "Underground Tunnel West-East") + connect(multiworld, player, "Route 8", "Underground Tunnel West-East") + connect(multiworld, player, "Route 8", "Celadon City") + connect(multiworld, player, "Route 8", "Route 8 Grass", lambda state: state.pokemon_rb_can_cut(player), one_way=True) + connect(multiworld, player, "Route 7", "Celadon City") + connect(multiworld, player, "Celadon City", "Celadon Gym", lambda state: state.pokemon_rb_can_cut(player), one_way=True) + connect(multiworld, player, "Celadon City", "Celadon Prize Corner") + connect(multiworld, player, "Celadon City", "Route 16") + connect(multiworld, player, "Route 16", "Route 16 North", lambda state: state.pokemon_rb_can_cut(player), one_way=True) + connect(multiworld, player, "Route 16", "Route 16 West", lambda state: state.has("Poke Flute", player) and state.has("Bicycle", player)) + connect(multiworld, player, "Route 17", "Route 16 West") + connect(multiworld, player, "Route 17", "Route 18", lambda state: state.has("Bicycle", player)) + connect(multiworld, player, "Fuchsia City", "Fuchsia Gym", one_way=True) + connect(multiworld, player, "Fuchsia City", "Route 18") + connect(multiworld, player, "Fuchsia City", "Safari Zone Gate", one_way=True) + connect(multiworld, player, "Safari Zone Gate", "Safari Zone Center", lambda state: state.has("Safari Pass", player) or not state.multiworld.extra_key_items[player].value, one_way=True) + connect(multiworld, player, "Safari Zone Center", "Safari Zone East", one_way=True) + connect(multiworld, player, "Safari Zone Center", "Safari Zone West", one_way=True) + connect(multiworld, player, "Safari Zone Center", "Safari Zone North", one_way=True) + connect(multiworld, player, "Fuchsia City", "Route 15") + connect(multiworld, player, "Route 15", "Route 14") + connect(multiworld, player, "Route 14", "Route 14 Grass", lambda state: state.pokemon_rb_can_cut(player), one_way=True) + connect(multiworld, player, "Route 14", "Route 13") + connect(multiworld, player, "Route 13", "Route 13 East", lambda state: state.pokemon_rb_can_strength(player) or state.pokemon_rb_can_surf(player) or not state.multiworld.extra_strength_boulders[player].value) + connect(multiworld, player, "Route 12 South", "Route 13 East") + connect(multiworld, player, "Fuchsia City", "Route 19", lambda state: state.pokemon_rb_can_surf(player)) + connect(multiworld, player, "Route 20 East", "Route 19") + connect(multiworld, player, "Route 20 West", "Cinnabar Island", lambda state: state.pokemon_rb_can_surf(player)) + connect(multiworld, player, "Route 20 West", "Seafoam Islands 1F") + connect(multiworld, player, "Route 20 East", "Seafoam Islands 1F", one_way=True) + connect(multiworld, player, "Seafoam Islands 1F", "Route 20 East", lambda state: state.pokemon_rb_can_strength(player), one_way=True) + connect(multiworld, player, "Viridian City", "Viridian City North", lambda state: state.has("Oak's Parcel", player) or state.multiworld.old_man[player].value == 2 or state.pokemon_rb_can_cut(player)) + connect(multiworld, player, "Route 3", "Mt Moon 1F", one_way=True) + connect(multiworld, player, "Route 11", "Route 11 East", lambda state: state.pokemon_rb_can_strength(player)) + connect(multiworld, player, "Cinnabar Island", "Cinnabar Gym", lambda state: state.has("Secret Key", player), one_way=True) + connect(multiworld, player, "Cinnabar Island", "Pokemon Mansion 1F", lambda state: state.has("Mansion Key", player) or not state.multiworld.extra_key_items[player].value, one_way=True) + connect(multiworld, player, "Seafoam Islands 1F", "Seafoam Islands B1F", one_way=True) + connect(multiworld, player, "Seafoam Islands B1F", "Seafoam Islands B2F", one_way=True) + connect(multiworld, player, "Seafoam Islands B2F", "Seafoam Islands B3F", one_way=True) + connect(multiworld, player, "Seafoam Islands B3F", "Seafoam Islands B4F", one_way=True) + connect(multiworld, player, "Route 21", "Cinnabar Island", lambda state: state.pokemon_rb_can_surf(player)) + connect(multiworld, player, "Pallet Town", "Route 21", lambda state: state.pokemon_rb_can_surf(player)) + connect(multiworld, player, "Saffron City", "Silph Co 1F", lambda state: state.has("Fuji Saved", player), one_way=True) + connect(multiworld, player, "Silph Co 1F", "Silph Co 2F", one_way=True) + connect(multiworld, player, "Silph Co 2F", "Silph Co 3F", one_way=True) + connect(multiworld, player, "Silph Co 3F", "Silph Co 4F", one_way=True) + connect(multiworld, player, "Silph Co 4F", "Silph Co 5F", one_way=True) + connect(multiworld, player, "Silph Co 5F", "Silph Co 6F", one_way=True) + connect(multiworld, player, "Silph Co 6F", "Silph Co 7F", one_way=True) + connect(multiworld, player, "Silph Co 7F", "Silph Co 8F", one_way=True) + connect(multiworld, player, "Silph Co 8F", "Silph Co 9F", one_way=True) + connect(multiworld, player, "Silph Co 9F", "Silph Co 10F", one_way=True) + connect(multiworld, player, "Silph Co 10F", "Silph Co 11F", one_way=True) + connect(multiworld, player, "Celadon City", "Rocket Hideout B1F", lambda state: state.has("Hideout Key", player) or not state.multiworld.extra_key_items[player].value, one_way=True) + connect(multiworld, player, "Rocket Hideout B1F", "Rocket Hideout B2F", one_way=True) + connect(multiworld, player, "Rocket Hideout B2F", "Rocket Hideout B3F", one_way=True) + connect(multiworld, player, "Rocket Hideout B3F", "Rocket Hideout B4F", one_way=True) + connect(multiworld, player, "Pokemon Mansion 1F", "Pokemon Mansion 2F", one_way=True) + connect(multiworld, player, "Pokemon Mansion 2F", "Pokemon Mansion 3F", one_way=True) + connect(multiworld, player, "Pokemon Mansion 1F", "Pokemon Mansion B1F", one_way=True) + connect(multiworld, player, "Route 23", "Victory Road 1F", lambda state: state.pokemon_rb_can_strength(player), one_way=True) + connect(multiworld, player, "Victory Road 1F", "Victory Road 2F", one_way=True) + connect(multiworld, player, "Victory Road 2F", "Victory Road 3F", one_way=True) + connect(multiworld, player, "Victory Road 2F", "Indigo Plateau", lambda state: state.pokemon_rb_has_badges(state.multiworld.elite_four_condition[player], player), one_way=True) + connect(multiworld, player, "Cerulean City", "Cerulean Cave 1F", lambda state: state.pokemon_rb_cerulean_cave(state.multiworld.cerulean_cave_condition[player].value + (state.multiworld.extra_key_items[player].value * 4), player) and state.pokemon_rb_can_surf(player), one_way=True) - connect(world, player, "Cerulean Cave 1F", "Cerulean Cave 2F", one_way=True) - connect(world, player, "Cerulean Cave 1F", "Cerulean Cave B1F", lambda state: state.pokemon_rb_can_surf(player), one_way=True) - if world.worlds[player].fly_map != "Pallet Town": - connect(world, player, "Menu", world.worlds[player].fly_map, lambda state: state.pokemon_rb_can_fly(player), one_way=True, - name="Fly to " + world.worlds[player].fly_map) + connect(multiworld, player, "Cerulean Cave 1F", "Cerulean Cave 2F", one_way=True) + connect(multiworld, player, "Cerulean Cave 1F", "Cerulean Cave B1F", lambda state: state.pokemon_rb_can_surf(player), one_way=True) + if multiworld.worlds[player].fly_map != "Pallet Town": + connect(multiworld, player, "Menu", multiworld.worlds[player].fly_map, lambda state: state.pokemon_rb_can_fly(player), one_way=True, + name="Fly to " + multiworld.worlds[player].fly_map) def connect(world: MultiWorld, player: int, source: str, target: str, rule: callable = lambda state: True, one_way=False, name=None): diff --git a/worlds/pokemon_rb/rom.py b/worlds/pokemon_rb/rom.py index f91c91e9cae1..d209f0b295b6 100644 --- a/worlds/pokemon_rb/rom.py +++ b/worlds/pokemon_rb/rom.py @@ -7,6 +7,7 @@ from .text import encode_text from .rom_addresses import rom_addresses from .locations import location_data +from .items import item_table import worlds.pokemon_rb.poke_data as poke_data @@ -386,6 +387,9 @@ def generate_output(self, output_directory: str): data[rom_addresses["Guard_Drink_List"] + 1] = 0 data[rom_addresses["Guard_Drink_List"] + 2] = 0 + data[rom_addresses["Fossils_Needed_For_Second_Item"]] = ( + self.multiworld.second_fossil_check_condition[self.player].value) + if self.multiworld.extra_key_items[self.player].value: data[rom_addresses['Options']] |= 4 data[rom_addresses["Option_Blind_Trainers"]] = round(self.multiworld.blind_trainers[self.player].value * 2.55) @@ -406,9 +410,7 @@ def generate_output(self, output_directory: str): if self.multiworld.old_man[self.player].value == 2: data[rom_addresses['Option_Old_Man']] = 0x11 data[rom_addresses['Option_Old_Man_Lying']] = 0x15 - money = str(self.multiworld.starting_money[self.player].value) - while len(money) < 6: - money = "0" + money + money = str(self.multiworld.starting_money[self.player].value).zfill(6) data[rom_addresses["Starting_Money_High"]] = int(money[:2], 16) data[rom_addresses["Starting_Money_Middle"]] = int(money[2:4], 16) data[rom_addresses["Starting_Money_Low"]] = int(money[4:], 16) @@ -417,6 +419,10 @@ def generate_output(self, output_directory: str): data[rom_addresses["Text_Badges_Needed"]] = encode_text( str(max(self.multiworld.victory_road_condition[self.player].value, self.multiworld.elite_four_condition[self.player].value)))[0] + write_bytes(data, encode_text( + " ".join(self.multiworld.get_location("Route 3 - Pokemon For Sale", self.player).item.name.upper().split()[1:])), + rom_addresses["Text_Magikarp_Salesman"]) + if self.multiworld.badges_needed_for_hm_moves[self.player].value == 0: for hm_move in poke_data.hm_moves: write_bytes(data, bytearray([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), @@ -441,61 +447,80 @@ def generate_output(self, output_directory: str): if badge not in written_badges: write_bytes(data, encode_text("Nothing"), rom_addresses["Badge_Text_" + badge.replace(" ", "_")]) - chart = deepcopy(poke_data.type_chart) - if self.multiworld.randomize_type_matchup_types[self.player].value == 1: - attacking_types = [] - defending_types = [] - for matchup in chart: - attacking_types.append(matchup[0]) - defending_types.append(matchup[1]) - random.shuffle(attacking_types) - random.shuffle(defending_types) + if self.multiworld.randomize_type_chart[self.player] == "vanilla": + chart = deepcopy(poke_data.type_chart) + elif self.multiworld.randomize_type_chart[self.player] == "randomize": + types = poke_data.type_names.values() matchups = [] - while len(attacking_types) > 0: - if [attacking_types[0], defending_types[0]] not in matchups: - matchups.append([attacking_types.pop(0), defending_types.pop(0)]) - else: - matchup = matchups.pop(0) - attacking_types.append(matchup[0]) - defending_types.append(matchup[1]) - random.shuffle(attacking_types) - random.shuffle(defending_types) - for matchup, chart_row in zip(matchups, chart): - chart_row[0] = matchup[0] - chart_row[1] = matchup[1] - elif self.multiworld.randomize_type_matchup_types[self.player].value == 2: - used_matchups = [] - for matchup in chart: - matchup[0] = random.choice(list(poke_data.type_names.values())) - matchup[1] = random.choice(list(poke_data.type_names.values())) - while [matchup[0], matchup[1]] in used_matchups: - matchup[0] = random.choice(list(poke_data.type_names.values())) - matchup[1] = random.choice(list(poke_data.type_names.values())) - used_matchups.append([matchup[0], matchup[1]]) - if self.multiworld.randomize_type_matchup_type_effectiveness[self.player].value == 1: - effectiveness_list = [] - for matchup in chart: - effectiveness_list.append(matchup[2]) - random.shuffle(effectiveness_list) - for (matchup, effectiveness) in zip(chart, effectiveness_list): - matchup[2] = effectiveness - elif self.multiworld.randomize_type_matchup_type_effectiveness[self.player].value == 2: - for matchup in chart: - matchup[2] = random.choice([0] + ([5, 20] * 5)) - elif self.multiworld.randomize_type_matchup_type_effectiveness[self.player].value == 3: - for matchup in chart: - matchup[2] = random.choice([i for i in range(0, 21) if i != 10]) - type_loc = rom_addresses["Type_Chart"] - for matchup in chart: - data[type_loc] = poke_data.type_ids[matchup[0]] - data[type_loc + 1] = poke_data.type_ids[matchup[1]] - data[type_loc + 2] = matchup[2] - type_loc += 3 + for type1 in types: + for type2 in types: + matchups.append([type1, type2]) + self.multiworld.random.shuffle(matchups) + immunities = self.multiworld.immunity_matchups[self.player].value + super_effectives = self.multiworld.super_effective_matchups[self.player].value + not_very_effectives = self.multiworld.not_very_effective_matchups[self.player].value + normals = self.multiworld.normal_matchups[self.player].value + while super_effectives + not_very_effectives + normals < 225 - immunities: + super_effectives += self.multiworld.super_effective_matchups[self.player].value + not_very_effectives += self.multiworld.not_very_effective_matchups[self.player].value + normals += self.multiworld.normal_matchups[self.player].value + if super_effectives + not_very_effectives + normals > 225 - immunities: + total = super_effectives + not_very_effectives + normals + excess = total - (225 - immunities) + subtract_amounts = (int((excess / (super_effectives + not_very_effectives + normals)) * super_effectives), + int((excess / (super_effectives + not_very_effectives + normals)) * not_very_effectives), + int((excess / (super_effectives + not_very_effectives + normals)) * normals)) + super_effectives -= subtract_amounts[0] + not_very_effectives -= subtract_amounts[1] + normals -= subtract_amounts[2] + while super_effectives + not_very_effectives + normals > 225 - immunities: + r = self.multiworld.random.randint(0, 2) + if r == 0: + super_effectives -= 1 + elif r == 1: + not_very_effectives -= 1 + else: + normals -= 1 + chart = [] + for matchup_list, matchup_value in zip([immunities, normals, super_effectives, not_very_effectives], + [0, 10, 20, 5]): + for _ in range(matchup_list): + matchup = matchups.pop() + matchup.append(matchup_value) + chart.append(matchup) + elif self.multiworld.randomize_type_chart[self.player] == "chaos": + types = poke_data.type_names.values() + matchups = [] + for type1 in types: + for type2 in types: + matchups.append([type1, type2]) + chart = [] + values = list(range(21)) + self.multiworld.random.shuffle(matchups) + self.multiworld.random.shuffle(values) + for matchup in matchups: + value = values.pop(0) + values.append(value) + matchup.append(value) + chart.append(matchup) # sort so that super-effective matchups occur first, to prevent dual "not very effective" / "super effective" # matchups from leading to damage being ultimately divided by 2 and then multiplied by 2, which can lead to # damage being reduced by 1 which leads to a "not very effective" message appearing due to my changes # to the way effectiveness messages are generated. - self.type_chart = sorted(chart, key=lambda matchup: 0 - matchup[2]) + chart = sorted(chart, key=lambda matchup: -matchup[2]) + + type_loc = rom_addresses["Type_Chart"] + for matchup in chart: + if matchup[2] != 10: # don't needlessly divide damage by 10 and multiply by 10 + data[type_loc] = poke_data.type_ids[matchup[0]] + data[type_loc + 1] = poke_data.type_ids[matchup[1]] + data[type_loc + 2] = matchup[2] + type_loc += 3 + data[type_loc] = 0xFF + data[type_loc + 1] = 0xFF + data[type_loc + 2] = 0xFF + + self.type_chart = chart if self.multiworld.normalize_encounter_chances[self.player].value: chances = [25, 51, 77, 103, 129, 155, 180, 205, 230, 255] @@ -525,9 +550,9 @@ def generate_output(self, output_directory: str): for i, move in enumerate(self.learnsets[mon]): data[(address + 1) + i * 2] = poke_data.moves[move]["id"] - data[rom_addresses["Option_Aide_Rt2"]] = self.multiworld.oaks_aide_rt_2[self.player] - data[rom_addresses["Option_Aide_Rt11"]] = self.multiworld.oaks_aide_rt_11[self.player] - data[rom_addresses["Option_Aide_Rt15"]] = self.multiworld.oaks_aide_rt_15[self.player] + data[rom_addresses["Option_Aide_Rt2"]] = self.multiworld.oaks_aide_rt_2[self.player].value + data[rom_addresses["Option_Aide_Rt11"]] = self.multiworld.oaks_aide_rt_11[self.player].value + data[rom_addresses["Option_Aide_Rt15"]] = self.multiworld.oaks_aide_rt_15[self.player].value if self.multiworld.safari_zone_normal_battles[self.player].value == 1: data[rom_addresses["Option_Safari_Zone_Battle_Type"]] = 255 @@ -535,6 +560,31 @@ def generate_output(self, output_directory: str): if self.multiworld.reusable_tms[self.player].value: data[rom_addresses["Option_Reusable_TMs"]] = 0xC9 + data[rom_addresses["Option_Trainersanity"]] = self.multiworld.trainersanity[self.player].value + data[rom_addresses["Option_Trainersanity2"]] = self.multiworld.trainersanity[self.player].value + + data[rom_addresses["Option_Always_Half_STAB"]] = int(not self.multiworld.same_type_attack_bonus[self.player].value) + + if self.multiworld.better_shops[self.player].value: + inventory = ["Poke Ball", "Great Ball", "Ultra Ball"] + if self.multiworld.better_shops[self.player].value == 2: + inventory.append("Master Ball") + inventory += ["Potion", "Super Potion", "Hyper Potion", "Max Potion", "Full Restore", "Antidote", "Awakening", + "Burn Heal", "Ice Heal", "Paralyze Heal", "Full Heal", "Repel", "Super Repel", "Max Repel", + "Escape Rope"] + shop_data = bytearray([0xFE, len(inventory)]) + shop_data += bytearray([item_table[item].id - 172000000 for item in inventory]) + shop_data.append(0xFF) + for shop in range(1, 10): + write_bytes(data, shop_data, rom_addresses[f"Shop{shop}"]) + price = str(self.multiworld.master_ball_price[self.player].value).zfill(6) + price = bytearray([int(price[:2], 16), int(price[2:4], 16), int(price[4:], 16)]) + write_bytes(data, price, rom_addresses["Price_Master_Ball"]) # Money values in Red and Blue are weird + + for item in reversed(self.multiworld.precollected_items[self.player]): + if data[rom_addresses["Start_Inventory"] + item.code - 172000000] < 255: + data[rom_addresses["Start_Inventory"] + item.code - 172000000] += 1 + process_trainer_data(self, data) mons = [mon["id"] for mon in poke_data.pokemon_data.values()] @@ -558,9 +608,16 @@ def generate_output(self, output_directory: str): slot_name.replace(">", " ") write_bytes(data, encode_text(slot_name, 16, True, True), rom_addresses['Title_Slot_Name']) - write_bytes(data, self.trainer_name, rom_addresses['Player_Name']) - write_bytes(data, self.rival_name, rom_addresses['Rival_Name']) + if self.trainer_name == "choose_in_game": + data[rom_addresses["Skip_Player_Name"]] = 0 + else: + write_bytes(data, self.trainer_name, rom_addresses['Player_Name']) + if self.rival_name == "choose_in_game": + data[rom_addresses["Skip_Rival_Name"]] = 0 + else: + write_bytes(data, self.rival_name, rom_addresses['Rival_Name']) + data[0xFF00] = 1 # client compatibility version write_bytes(data, self.multiworld.seed_name.encode(), 0xFFDB) write_bytes(data, self.multiworld.player_name[self.player].encode(), 0xFFF0) diff --git a/worlds/pokemon_rb/rom_addresses.py b/worlds/pokemon_rb/rom_addresses.py index 206365a287af..b475b58ceece 100644 --- a/worlds/pokemon_rb/rom_addresses.py +++ b/worlds/pokemon_rb/rom_addresses.py @@ -1,97 +1,102 @@ rom_addresses = { "Option_Encounter_Minimum_Steps": 0x3c3, - "Option_Blind_Trainers": 0x317e, - "Base_Stats_Mew": 0x425b, - "Title_Mon_First": 0x436e, - "Title_Mons": 0x4547, - "Player_Name": 0x4569, - "Rival_Name": 0x4571, - "Title_Seed": 0x5dfe, - "Title_Slot_Name": 0x5e1e, - "PC_Item": 0x61ec, - "PC_Item_Quantity": 0x61f1, - "Options": 0x61f9, - "Fly_Location": 0x61fe, - "Option_Old_Man": 0xcaef, - "Option_Old_Man_Lying": 0xcaf2, - "Option_Boulders": 0xcd98, - "Option_Rock_Tunnel_Extra_Items": 0xcda1, - "Wild_Route1": 0xd0fb, - "Wild_Route2": 0xd111, - "Wild_Route22": 0xd127, - "Wild_ViridianForest": 0xd13d, - "Wild_Route3": 0xd153, - "Wild_MtMoon1F": 0xd169, - "Wild_MtMoonB1F": 0xd17f, - "Wild_MtMoonB2F": 0xd195, - "Wild_Route4": 0xd1ab, - "Wild_Route24": 0xd1c1, - "Wild_Route25": 0xd1d7, - "Wild_Route9": 0xd1ed, - "Wild_Route5": 0xd203, - "Wild_Route6": 0xd219, - "Wild_Route11": 0xd22f, - "Wild_RockTunnel1F": 0xd245, - "Wild_RockTunnelB1F": 0xd25b, - "Wild_Route10": 0xd271, - "Wild_Route12": 0xd287, - "Wild_Route8": 0xd29d, - "Wild_Route7": 0xd2b3, - "Wild_PokemonTower3F": 0xd2cd, - "Wild_PokemonTower4F": 0xd2e3, - "Wild_PokemonTower5F": 0xd2f9, - "Wild_PokemonTower6F": 0xd30f, - "Wild_PokemonTower7F": 0xd325, - "Wild_Route13": 0xd33b, - "Wild_Route14": 0xd351, - "Wild_Route15": 0xd367, - "Wild_Route16": 0xd37d, - "Wild_Route17": 0xd393, - "Wild_Route18": 0xd3a9, - "Wild_SafariZoneCenter": 0xd3bf, - "Wild_SafariZoneEast": 0xd3d5, - "Wild_SafariZoneNorth": 0xd3eb, - "Wild_SafariZoneWest": 0xd401, - "Wild_SeaRoutes": 0xd418, - "Wild_SeafoamIslands1F": 0xd42d, - "Wild_SeafoamIslandsB1F": 0xd443, - "Wild_SeafoamIslandsB2F": 0xd459, - "Wild_SeafoamIslandsB3F": 0xd46f, - "Wild_SeafoamIslandsB4F": 0xd485, - "Wild_PokemonMansion1F": 0xd49b, - "Wild_PokemonMansion2F": 0xd4b1, - "Wild_PokemonMansion3F": 0xd4c7, - "Wild_PokemonMansionB1F": 0xd4dd, - "Wild_Route21": 0xd4f3, - "Wild_Surf_Route21": 0xd508, - "Wild_CeruleanCave1F": 0xd51d, - "Wild_CeruleanCave2F": 0xd533, - "Wild_CeruleanCaveB1F": 0xd549, - "Wild_PowerPlant": 0xd55f, - "Wild_Route23": 0xd575, - "Wild_VictoryRoad2F": 0xd58b, - "Wild_VictoryRoad3F": 0xd5a1, - "Wild_VictoryRoad1F": 0xd5b7, - "Wild_DiglettsCave": 0xd5cd, - "Ghost_Battle5": 0xd723, - "HM_Surf_Badge_a": 0xda11, - "HM_Surf_Badge_b": 0xda16, - "Wild_Old_Rod": 0xe313, - "Wild_Good_Rod": 0xe340, - "Option_Reusable_TMs": 0xe60c, - "Wild_Super_Rod_A": 0xea40, - "Wild_Super_Rod_B": 0xea45, - "Wild_Super_Rod_C": 0xea4a, - "Wild_Super_Rod_D": 0xea51, - "Wild_Super_Rod_E": 0xea56, - "Wild_Super_Rod_F": 0xea5b, - "Wild_Super_Rod_G": 0xea64, - "Wild_Super_Rod_H": 0xea6d, - "Wild_Super_Rod_I": 0xea76, - "Wild_Super_Rod_J": 0xea7f, - "Starting_Money_High": 0xf949, - "Starting_Money_Middle": 0xf94c, - "Starting_Money_Low": 0xf94f, + "Option_Blind_Trainers": 0x30fc, + "Option_Trainersanity": 0x318c, + "Option_Lose_Money": 0x40d4, + "Base_Stats_Mew": 0x4260, + "Title_Mon_First": 0x4373, + "Title_Mons": 0x454c, + "Player_Name": 0x456e, + "Rival_Name": 0x4576, + "Price_Master_Ball": 0x45d0, + "Title_Seed": 0x5e3a, + "Title_Slot_Name": 0x5e5a, + "PC_Item": 0x6228, + "PC_Item_Quantity": 0x622d, + "Options": 0x623d, + "Fly_Location": 0x6242, + "Skip_Player_Name": 0x625b, + "Skip_Rival_Name": 0x6269, + "Option_Old_Man": 0xcafc, + "Option_Old_Man_Lying": 0xcaff, + "Option_Boulders": 0xcda5, + "Option_Rock_Tunnel_Extra_Items": 0xcdae, + "Wild_Route1": 0xd108, + "Wild_Route2": 0xd11e, + "Wild_Route22": 0xd134, + "Wild_ViridianForest": 0xd14a, + "Wild_Route3": 0xd160, + "Wild_MtMoon1F": 0xd176, + "Wild_MtMoonB1F": 0xd18c, + "Wild_MtMoonB2F": 0xd1a2, + "Wild_Route4": 0xd1b8, + "Wild_Route24": 0xd1ce, + "Wild_Route25": 0xd1e4, + "Wild_Route9": 0xd1fa, + "Wild_Route5": 0xd210, + "Wild_Route6": 0xd226, + "Wild_Route11": 0xd23c, + "Wild_RockTunnel1F": 0xd252, + "Wild_RockTunnelB1F": 0xd268, + "Wild_Route10": 0xd27e, + "Wild_Route12": 0xd294, + "Wild_Route8": 0xd2aa, + "Wild_Route7": 0xd2c0, + "Wild_PokemonTower3F": 0xd2da, + "Wild_PokemonTower4F": 0xd2f0, + "Wild_PokemonTower5F": 0xd306, + "Wild_PokemonTower6F": 0xd31c, + "Wild_PokemonTower7F": 0xd332, + "Wild_Route13": 0xd348, + "Wild_Route14": 0xd35e, + "Wild_Route15": 0xd374, + "Wild_Route16": 0xd38a, + "Wild_Route17": 0xd3a0, + "Wild_Route18": 0xd3b6, + "Wild_SafariZoneCenter": 0xd3cc, + "Wild_SafariZoneEast": 0xd3e2, + "Wild_SafariZoneNorth": 0xd3f8, + "Wild_SafariZoneWest": 0xd40e, + "Wild_SeaRoutes": 0xd425, + "Wild_SeafoamIslands1F": 0xd43a, + "Wild_SeafoamIslandsB1F": 0xd450, + "Wild_SeafoamIslandsB2F": 0xd466, + "Wild_SeafoamIslandsB3F": 0xd47c, + "Wild_SeafoamIslandsB4F": 0xd492, + "Wild_PokemonMansion1F": 0xd4a8, + "Wild_PokemonMansion2F": 0xd4be, + "Wild_PokemonMansion3F": 0xd4d4, + "Wild_PokemonMansionB1F": 0xd4ea, + "Wild_Route21": 0xd500, + "Wild_Surf_Route21": 0xd515, + "Wild_CeruleanCave1F": 0xd52a, + "Wild_CeruleanCave2F": 0xd540, + "Wild_CeruleanCaveB1F": 0xd556, + "Wild_PowerPlant": 0xd56c, + "Wild_Route23": 0xd582, + "Wild_VictoryRoad2F": 0xd598, + "Wild_VictoryRoad3F": 0xd5ae, + "Wild_VictoryRoad1F": 0xd5c4, + "Wild_DiglettsCave": 0xd5da, + "Ghost_Battle5": 0xd730, + "HM_Surf_Badge_a": 0xda1e, + "HM_Surf_Badge_b": 0xda23, + "Wild_Old_Rod": 0xe320, + "Wild_Good_Rod": 0xe34d, + "Option_Reusable_TMs": 0xe619, + "Wild_Super_Rod_A": 0xea4e, + "Wild_Super_Rod_B": 0xea53, + "Wild_Super_Rod_C": 0xea58, + "Wild_Super_Rod_D": 0xea5f, + "Wild_Super_Rod_E": 0xea64, + "Wild_Super_Rod_F": 0xea69, + "Wild_Super_Rod_G": 0xea72, + "Wild_Super_Rod_H": 0xea7b, + "Wild_Super_Rod_I": 0xea84, + "Wild_Super_Rod_J": 0xea8d, + "Starting_Money_High": 0xf957, + "Starting_Money_Middle": 0xf95a, + "Starting_Money_Low": 0xf95d, "HM_Fly_Badge_a": 0x1318e, "HM_Fly_Badge_b": 0x13193, "HM_Cut_Badge_a": 0x131c4, @@ -107,55 +112,79 @@ "Starter3_K": 0x195b0, "Event_Rocket_Thief": 0x196cc, "Option_Cerulean_Cave_Condition": 0x1986c, - "Event_Stranded_Man": 0x19b2b, - "Event_Rivals_Sister": 0x19cf9, - "Option_Pokemon_League_Badges": 0x19e16, - "Missable_Silph_Co_4F_Item_1": 0x1a0d7, - "Missable_Silph_Co_4F_Item_2": 0x1a0de, - "Missable_Silph_Co_4F_Item_3": 0x1a0e5, - "Missable_Silph_Co_5F_Item_1": 0x1a337, - "Missable_Silph_Co_5F_Item_2": 0x1a33e, - "Missable_Silph_Co_5F_Item_3": 0x1a345, - "Missable_Silph_Co_6F_Item_1": 0x1a5ad, - "Missable_Silph_Co_6F_Item_2": 0x1a5b4, - "Event_Free_Sample": 0x1cade, - "Starter1_F": 0x1cca5, - "Starter2_F": 0x1cca9, - "Starter2_G": 0x1cde2, - "Starter3_G": 0x1cdea, - "Starter2_H": 0x1d0e5, - "Starter1_H": 0x1d0ef, - "Starter3_I": 0x1d0f6, - "Starter2_I": 0x1d100, - "Starter1_D": 0x1d107, - "Starter3_D": 0x1d111, - "Starter2_E": 0x1d2eb, - "Starter3_E": 0x1d2f3, - "Event_Oaks_Gift": 0x1d373, - "Event_Pokemart_Quest": 0x1d566, - "Event_Bicycle_Shop": 0x1d805, - "Text_Bicycle": 0x1d898, - "Event_Fuji": 0x1d9cd, - "Static_Encounter_Mew": 0x1dc4e, - "Gift_Eevee": 0x1dcc7, - "Event_Mr_Psychic": 0x1ddcf, - "Static_Encounter_Voltorb_A": 0x1e397, - "Static_Encounter_Voltorb_B": 0x1e39f, - "Static_Encounter_Voltorb_C": 0x1e3a7, - "Static_Encounter_Electrode_A": 0x1e3af, - "Static_Encounter_Voltorb_D": 0x1e3b7, - "Static_Encounter_Voltorb_E": 0x1e3bf, - "Static_Encounter_Electrode_B": 0x1e3c7, - "Static_Encounter_Voltorb_F": 0x1e3cf, - "Static_Encounter_Zapdos": 0x1e3d7, - "Missable_Power_Plant_Item_1": 0x1e3df, - "Missable_Power_Plant_Item_2": 0x1e3e6, - "Missable_Power_Plant_Item_3": 0x1e3ed, - "Missable_Power_Plant_Item_4": 0x1e3f4, - "Missable_Power_Plant_Item_5": 0x1e3fb, - "Event_Rt16_House_Woman": 0x1e5d4, - "Option_Victory_Road_Badges": 0x1e6a5, - "Event_Bill": 0x1e8d6, + "Event_Stranded_Man": 0x19b1f, + "Event_Rivals_Sister": 0x19ced, + "Option_Pokemon_League_Badges": 0x19e0a, + "Shop10": 0x19ee1, + "Trainersanity_EVENT_BEAT_SILPH_CO_4F_TRAINER_0_ITEM": 0x1a035, + "Trainersanity_EVENT_BEAT_SILPH_CO_4F_TRAINER_1_ITEM": 0x1a043, + "Trainersanity_EVENT_BEAT_SILPH_CO_4F_TRAINER_2_ITEM": 0x1a051, + "Missable_Silph_Co_4F_Item_1": 0x1a0f9, + "Missable_Silph_Co_4F_Item_2": 0x1a100, + "Missable_Silph_Co_4F_Item_3": 0x1a107, + "Trainersanity_EVENT_BEAT_SILPH_CO_5F_TRAINER_0_ITEM": 0x1a25f, + "Trainersanity_EVENT_BEAT_SILPH_CO_5F_TRAINER_1_ITEM": 0x1a26d, + "Trainersanity_EVENT_BEAT_SILPH_CO_5F_TRAINER_2_ITEM": 0x1a27b, + "Trainersanity_EVENT_BEAT_SILPH_CO_5F_TRAINER_3_ITEM": 0x1a289, + "Missable_Silph_Co_5F_Item_1": 0x1a361, + "Missable_Silph_Co_5F_Item_2": 0x1a368, + "Missable_Silph_Co_5F_Item_3": 0x1a36f, + "Trainersanity_EVENT_BEAT_SILPH_CO_6F_TRAINER_0_ITEM": 0x1a49f, + "Trainersanity_EVENT_BEAT_SILPH_CO_6F_TRAINER_1_ITEM": 0x1a4ad, + "Trainersanity_EVENT_BEAT_SILPH_CO_6F_TRAINER_2_ITEM": 0x1a4bb, + "Missable_Silph_Co_6F_Item_1": 0x1a5dd, + "Missable_Silph_Co_6F_Item_2": 0x1a5e4, + "Event_Free_Sample": 0x1cad6, + "Starter1_F": 0x1cca2, + "Starter2_F": 0x1cca6, + "Starter2_G": 0x1cddf, + "Starter3_G": 0x1cde7, + "Starter2_H": 0x1d0df, + "Starter1_H": 0x1d0e9, + "Starter3_I": 0x1d0f0, + "Starter2_I": 0x1d0fa, + "Starter1_D": 0x1d101, + "Starter3_D": 0x1d10b, + "Starter2_E": 0x1d2e5, + "Starter3_E": 0x1d2ed, + "Event_Pokedex": 0x1d351, + "Event_Oaks_Gift": 0x1d381, + "Event_Pokemart_Quest": 0x1d579, + "Shop1": 0x1d5a3, + "Event_Bicycle_Shop": 0x1d83d, + "Text_Bicycle": 0x1d8d0, + "Event_Fuji": 0x1da05, + "Trainersanity_EVENT_BEAT_MEW_ITEM": 0x1dc58, + "Static_Encounter_Mew": 0x1dc88, + "Gift_Eevee": 0x1dd01, + "Shop7": 0x1dd53, + "Event_Mr_Psychic": 0x1de30, + "Trainersanity_EVENT_BEAT_POWER_PLANT_VOLTORB_0_ITEM": 0x1e32b, + "Trainersanity_EVENT_BEAT_POWER_PLANT_VOLTORB_1_ITEM": 0x1e339, + "Trainersanity_EVENT_BEAT_POWER_PLANT_VOLTORB_2_ITEM": 0x1e347, + "Trainersanity_EVENT_BEAT_POWER_PLANT_VOLTORB_3_ITEM": 0x1e355, + "Trainersanity_EVENT_BEAT_POWER_PLANT_VOLTORB_4_ITEM": 0x1e363, + "Trainersanity_EVENT_BEAT_POWER_PLANT_VOLTORB_5_ITEM": 0x1e371, + "Trainersanity_EVENT_BEAT_POWER_PLANT_VOLTORB_6_ITEM": 0x1e37f, + "Trainersanity_EVENT_BEAT_POWER_PLANT_VOLTORB_7_ITEM": 0x1e38d, + "Trainersanity_EVENT_BEAT_ZAPDOS_ITEM": 0x1e39b, + "Static_Encounter_Voltorb_A": 0x1e40a, + "Static_Encounter_Voltorb_B": 0x1e412, + "Static_Encounter_Voltorb_C": 0x1e41a, + "Static_Encounter_Electrode_A": 0x1e422, + "Static_Encounter_Voltorb_D": 0x1e42a, + "Static_Encounter_Voltorb_E": 0x1e432, + "Static_Encounter_Electrode_B": 0x1e43a, + "Static_Encounter_Voltorb_F": 0x1e442, + "Static_Encounter_Zapdos": 0x1e44a, + "Missable_Power_Plant_Item_1": 0x1e452, + "Missable_Power_Plant_Item_2": 0x1e459, + "Missable_Power_Plant_Item_3": 0x1e460, + "Missable_Power_Plant_Item_4": 0x1e467, + "Missable_Power_Plant_Item_5": 0x1e46e, + "Event_Rt16_House_Woman": 0x1e647, + "Option_Victory_Road_Badges": 0x1e718, + "Event_Bill": 0x1e949, "Starter1_O": 0x372b0, "Starter2_O": 0x372b4, "Starter3_O": 0x372b8, @@ -327,98 +356,131 @@ "Learnset_Bellsprout": 0x3b9dc, "Learnset_Weepinbell": 0x3b9f0, "Learnset_Victreebel": 0x3ba00, - "Type_Chart": 0x3e4b0, - "Type_Chart_Divider": 0x3e5a6, - "Ghost_Battle3": 0x3efd3, - "Missable_Pokemon_Mansion_1F_Item_1": 0x443d6, - "Missable_Pokemon_Mansion_1F_Item_2": 0x443dd, - "Map_Rock_TunnelF": 0x44676, - "Missable_Victory_Road_3F_Item_1": 0x44b07, - "Missable_Victory_Road_3F_Item_2": 0x44b0e, - "Missable_Rocket_Hideout_B1F_Item_1": 0x44d2d, - "Missable_Rocket_Hideout_B1F_Item_2": 0x44d34, - "Missable_Rocket_Hideout_B2F_Item_1": 0x4511d, - "Missable_Rocket_Hideout_B2F_Item_2": 0x45124, - "Missable_Rocket_Hideout_B2F_Item_3": 0x4512b, - "Missable_Rocket_Hideout_B2F_Item_4": 0x45132, - "Missable_Rocket_Hideout_B3F_Item_1": 0x4536f, - "Missable_Rocket_Hideout_B3F_Item_2": 0x45376, - "Missable_Rocket_Hideout_B4F_Item_1": 0x45627, - "Missable_Rocket_Hideout_B4F_Item_2": 0x4562e, - "Missable_Rocket_Hideout_B4F_Item_3": 0x45635, - "Missable_Rocket_Hideout_B4F_Item_4": 0x4563c, - "Missable_Rocket_Hideout_B4F_Item_5": 0x45643, - "Missable_Safari_Zone_East_Item_1": 0x458b2, - "Missable_Safari_Zone_East_Item_2": 0x458b9, - "Missable_Safari_Zone_East_Item_3": 0x458c0, - "Missable_Safari_Zone_East_Item_4": 0x458c7, - "Missable_Safari_Zone_North_Item_1": 0x45a12, - "Missable_Safari_Zone_North_Item_2": 0x45a19, - "Missable_Safari_Zone_Center_Item": 0x45bf9, - "Missable_Cerulean_Cave_2F_Item_1": 0x45e36, - "Missable_Cerulean_Cave_2F_Item_2": 0x45e3d, - "Missable_Cerulean_Cave_2F_Item_3": 0x45e44, - "Static_Encounter_Mewtwo": 0x45f44, - "Missable_Cerulean_Cave_B1F_Item_1": 0x45f4c, - "Missable_Cerulean_Cave_B1F_Item_2": 0x45f53, - "Missable_Rock_Tunnel_B1F_Item_1": 0x4619f, - "Missable_Rock_Tunnel_B1F_Item_2": 0x461a6, - "Missable_Rock_Tunnel_B1F_Item_3": 0x461ad, - "Missable_Rock_Tunnel_B1F_Item_4": 0x461b4, - "Static_Encounter_Articuno": 0x4690c, - "Hidden_Item_Viridian_Forest_1": 0x46e6d, - "Hidden_Item_Viridian_Forest_2": 0x46e73, - "Hidden_Item_MtMoonB2F_1": 0x46e7a, - "Hidden_Item_MtMoonB2F_2": 0x46e80, - "Hidden_Item_Route_25_1": 0x46e94, - "Hidden_Item_Route_25_2": 0x46e9a, - "Hidden_Item_Route_9": 0x46ea1, - "Hidden_Item_SS_Anne_Kitchen": 0x46eb4, - "Hidden_Item_SS_Anne_B1F": 0x46ebb, - "Hidden_Item_Route_10_1": 0x46ec2, - "Hidden_Item_Route_10_2": 0x46ec8, - "Hidden_Item_Rocket_Hideout_B1F": 0x46ecf, - "Hidden_Item_Rocket_Hideout_B3F": 0x46ed6, - "Hidden_Item_Rocket_Hideout_B4F": 0x46edd, - "Hidden_Item_Pokemon_Tower_5F": 0x46ef1, - "Hidden_Item_Route_13_1": 0x46ef8, - "Hidden_Item_Route_13_2": 0x46efe, - "Hidden_Item_Safari_Zone_West": 0x46f0c, - "Hidden_Item_Silph_Co_5F": 0x46f13, - "Hidden_Item_Silph_Co_9F": 0x46f1a, - "Hidden_Item_Copycats_House": 0x46f21, - "Hidden_Item_Cerulean_Cave_1F": 0x46f28, - "Hidden_Item_Cerulean_Cave_B1F": 0x46f2f, - "Hidden_Item_Power_Plant_1": 0x46f36, - "Hidden_Item_Power_Plant_2": 0x46f3c, - "Hidden_Item_Seafoam_Islands_B2F": 0x46f43, - "Hidden_Item_Seafoam_Islands_B4F": 0x46f4a, - "Hidden_Item_Pokemon_Mansion_1F": 0x46f51, - "Hidden_Item_Pokemon_Mansion_3F": 0x46f65, - "Hidden_Item_Pokemon_Mansion_B1F": 0x46f72, - "Hidden_Item_Route_23_1": 0x46f85, - "Hidden_Item_Route_23_2": 0x46f8b, - "Hidden_Item_Route_23_3": 0x46f91, - "Hidden_Item_Victory_Road_2F_1": 0x46f98, - "Hidden_Item_Victory_Road_2F_2": 0x46f9e, - "Hidden_Item_Unused_6F": 0x46fa5, - "Hidden_Item_Viridian_City": 0x46fb3, - "Hidden_Item_Route_11": 0x47060, - "Hidden_Item_Route_12": 0x47067, - "Hidden_Item_Route_17_1": 0x47075, - "Hidden_Item_Route_17_2": 0x4707b, - "Hidden_Item_Route_17_3": 0x47081, - "Hidden_Item_Route_17_4": 0x47087, - "Hidden_Item_Route_17_5": 0x4708d, - "Hidden_Item_Underground_Path_NS_1": 0x47094, - "Hidden_Item_Underground_Path_NS_2": 0x4709a, - "Hidden_Item_Underground_Path_WE_1": 0x470a1, - "Hidden_Item_Underground_Path_WE_2": 0x470a7, - "Hidden_Item_Celadon_City": 0x470ae, - "Hidden_Item_Seafoam_Islands_B3F": 0x470b5, - "Hidden_Item_Vermilion_City": 0x470bc, - "Hidden_Item_Cerulean_City": 0x470c3, - "Hidden_Item_Route_4": 0x470ca, + "Option_Always_Half_STAB": 0x3e3fb, + "Type_Chart": 0x3e4ee, + "Ghost_Battle3": 0x3f1be, + "Trainersanity_EVENT_BEAT_MANSION_1_TRAINER_0_ITEM": 0x44341, + "Missable_Pokemon_Mansion_1F_Item_1": 0x443d8, + "Missable_Pokemon_Mansion_1F_Item_2": 0x443df, + "Trainersanity_EVENT_BEAT_ROCK_TUNNEL_1_TRAINER_0_ITEM": 0x44514, + "Trainersanity_EVENT_BEAT_ROCK_TUNNEL_1_TRAINER_1_ITEM": 0x44522, + "Trainersanity_EVENT_BEAT_ROCK_TUNNEL_1_TRAINER_2_ITEM": 0x44530, + "Trainersanity_EVENT_BEAT_ROCK_TUNNEL_1_TRAINER_3_ITEM": 0x4453e, + "Trainersanity_EVENT_BEAT_ROCK_TUNNEL_1_TRAINER_4_ITEM": 0x4454c, + "Trainersanity_EVENT_BEAT_ROCK_TUNNEL_1_TRAINER_5_ITEM": 0x4455a, + "Trainersanity_EVENT_BEAT_ROCK_TUNNEL_1_TRAINER_6_ITEM": 0x44568, + "Map_Rock_TunnelF": 0x44686, + "Trainersanity_EVENT_BEAT_VICTORY_ROAD_3_TRAINER_0_ITEM": 0x44a55, + "Trainersanity_EVENT_BEAT_VICTORY_ROAD_3_TRAINER_1_ITEM": 0x44a63, + "Trainersanity_EVENT_BEAT_VICTORY_ROAD_3_TRAINER_2_ITEM": 0x44a71, + "Trainersanity_EVENT_BEAT_VICTORY_ROAD_3_TRAINER_3_ITEM": 0x44a7f, + "Missable_Victory_Road_3F_Item_1": 0x44b1f, + "Missable_Victory_Road_3F_Item_2": 0x44b26, + "Trainersanity_EVENT_BEAT_ROCKET_HIDEOUT_1_TRAINER_0_ITEM": 0x44c47, + "Trainersanity_EVENT_BEAT_ROCKET_HIDEOUT_1_TRAINER_1_ITEM": 0x44c55, + "Trainersanity_EVENT_BEAT_ROCKET_HIDEOUT_1_TRAINER_2_ITEM": 0x44c63, + "Trainersanity_EVENT_BEAT_ROCKET_HIDEOUT_1_TRAINER_3_ITEM": 0x44c71, + "Trainersanity_EVENT_BEAT_ROCKET_HIDEOUT_1_TRAINER_4_ITEM": 0x44c7f, + "Missable_Rocket_Hideout_B1F_Item_1": 0x44d4f, + "Missable_Rocket_Hideout_B1F_Item_2": 0x44d56, + "Trainersanity_EVENT_BEAT_ROCKET_HIDEOUT_2_TRAINER_0_ITEM": 0x45100, + "Missable_Rocket_Hideout_B2F_Item_1": 0x45141, + "Missable_Rocket_Hideout_B2F_Item_2": 0x45148, + "Missable_Rocket_Hideout_B2F_Item_3": 0x4514f, + "Missable_Rocket_Hideout_B2F_Item_4": 0x45156, + "Trainersanity_EVENT_BEAT_ROCKET_HIDEOUT_3_TRAINER_0_ITEM": 0x45333, + "Trainersanity_EVENT_BEAT_ROCKET_HIDEOUT_3_TRAINER_1_ITEM": 0x45341, + "Missable_Rocket_Hideout_B3F_Item_1": 0x45397, + "Missable_Rocket_Hideout_B3F_Item_2": 0x4539e, + "Trainersanity_EVENT_BEAT_ROCKET_HIDEOUT_4_TRAINER_0_ITEM": 0x4554a, + "Trainersanity_EVENT_BEAT_ROCKET_HIDEOUT_4_TRAINER_1_ITEM": 0x45558, + "Trainersanity_EVENT_BEAT_ROCKET_HIDEOUT_4_TRAINER_2_ITEM": 0x45566, + "Missable_Rocket_Hideout_B4F_Item_1": 0x45655, + "Missable_Rocket_Hideout_B4F_Item_2": 0x4565c, + "Missable_Rocket_Hideout_B4F_Item_3": 0x45663, + "Missable_Rocket_Hideout_B4F_Item_4": 0x4566a, + "Missable_Rocket_Hideout_B4F_Item_5": 0x45671, + "Missable_Safari_Zone_East_Item_1": 0x458e0, + "Missable_Safari_Zone_East_Item_2": 0x458e7, + "Missable_Safari_Zone_East_Item_3": 0x458ee, + "Missable_Safari_Zone_East_Item_4": 0x458f5, + "Missable_Safari_Zone_North_Item_1": 0x45a40, + "Missable_Safari_Zone_North_Item_2": 0x45a47, + "Missable_Safari_Zone_Center_Item": 0x45c27, + "Missable_Cerulean_Cave_2F_Item_1": 0x45e64, + "Missable_Cerulean_Cave_2F_Item_2": 0x45e6b, + "Missable_Cerulean_Cave_2F_Item_3": 0x45e72, + "Trainersanity_EVENT_BEAT_MEWTWO_ITEM": 0x45f4a, + "Static_Encounter_Mewtwo": 0x45f74, + "Missable_Cerulean_Cave_B1F_Item_1": 0x45f7c, + "Missable_Cerulean_Cave_B1F_Item_2": 0x45f83, + "Trainersanity_EVENT_BEAT_ROCK_TUNNEL_2_TRAINER_0_ITEM": 0x46059, + "Trainersanity_EVENT_BEAT_ROCK_TUNNEL_2_TRAINER_1_ITEM": 0x46067, + "Trainersanity_EVENT_BEAT_ROCK_TUNNEL_2_TRAINER_2_ITEM": 0x46075, + "Trainersanity_EVENT_BEAT_ROCK_TUNNEL_2_TRAINER_3_ITEM": 0x46083, + "Trainersanity_EVENT_BEAT_ROCK_TUNNEL_2_TRAINER_4_ITEM": 0x46091, + "Trainersanity_EVENT_BEAT_ROCK_TUNNEL_2_TRAINER_5_ITEM": 0x4609f, + "Trainersanity_EVENT_BEAT_ROCK_TUNNEL_2_TRAINER_6_ITEM": 0x460ad, + "Trainersanity_EVENT_BEAT_ROCK_TUNNEL_2_TRAINER_7_ITEM": 0x460bb, + "Missable_Rock_Tunnel_B1F_Item_1": 0x461df, + "Missable_Rock_Tunnel_B1F_Item_2": 0x461e6, + "Missable_Rock_Tunnel_B1F_Item_3": 0x461ed, + "Missable_Rock_Tunnel_B1F_Item_4": 0x461f4, + "Trainersanity_EVENT_BEAT_ARTICUNO_ITEM": 0x468f7, + "Static_Encounter_Articuno": 0x4694e, + "Hidden_Item_Viridian_Forest_1": 0x46eaf, + "Hidden_Item_Viridian_Forest_2": 0x46eb5, + "Hidden_Item_MtMoonB2F_1": 0x46ebc, + "Hidden_Item_MtMoonB2F_2": 0x46ec2, + "Hidden_Item_Route_25_1": 0x46ed6, + "Hidden_Item_Route_25_2": 0x46edc, + "Hidden_Item_Route_9": 0x46ee3, + "Hidden_Item_SS_Anne_Kitchen": 0x46ef6, + "Hidden_Item_SS_Anne_B1F": 0x46efd, + "Hidden_Item_Route_10_1": 0x46f04, + "Hidden_Item_Route_10_2": 0x46f0a, + "Hidden_Item_Rocket_Hideout_B1F": 0x46f11, + "Hidden_Item_Rocket_Hideout_B3F": 0x46f18, + "Hidden_Item_Rocket_Hideout_B4F": 0x46f1f, + "Hidden_Item_Pokemon_Tower_5F": 0x46f33, + "Hidden_Item_Route_13_1": 0x46f3a, + "Hidden_Item_Route_13_2": 0x46f40, + "Hidden_Item_Safari_Zone_West": 0x46f4e, + "Hidden_Item_Silph_Co_5F": 0x46f55, + "Hidden_Item_Silph_Co_9F": 0x46f5c, + "Hidden_Item_Copycats_House": 0x46f63, + "Hidden_Item_Cerulean_Cave_1F": 0x46f6a, + "Hidden_Item_Cerulean_Cave_B1F": 0x46f71, + "Hidden_Item_Power_Plant_1": 0x46f78, + "Hidden_Item_Power_Plant_2": 0x46f7e, + "Hidden_Item_Seafoam_Islands_B2F": 0x46f85, + "Hidden_Item_Seafoam_Islands_B4F": 0x46f8c, + "Hidden_Item_Pokemon_Mansion_1F": 0x46f93, + "Hidden_Item_Pokemon_Mansion_3F": 0x46fa7, + "Hidden_Item_Pokemon_Mansion_B1F": 0x46fb4, + "Hidden_Item_Route_23_1": 0x46fc7, + "Hidden_Item_Route_23_2": 0x46fcd, + "Hidden_Item_Route_23_3": 0x46fd3, + "Hidden_Item_Victory_Road_2F_1": 0x46fda, + "Hidden_Item_Victory_Road_2F_2": 0x46fe0, + "Hidden_Item_Unused_6F": 0x46fe7, + "Hidden_Item_Viridian_City": 0x46ff5, + "Hidden_Item_Route_11": 0x470a2, + "Hidden_Item_Route_12": 0x470a9, + "Hidden_Item_Route_17_1": 0x470b7, + "Hidden_Item_Route_17_2": 0x470bd, + "Hidden_Item_Route_17_3": 0x470c3, + "Hidden_Item_Route_17_4": 0x470c9, + "Hidden_Item_Route_17_5": 0x470cf, + "Hidden_Item_Underground_Path_NS_1": 0x470d6, + "Hidden_Item_Underground_Path_NS_2": 0x470dc, + "Hidden_Item_Underground_Path_WE_1": 0x470e3, + "Hidden_Item_Underground_Path_WE_2": 0x470e9, + "Hidden_Item_Celadon_City": 0x470f0, + "Hidden_Item_Seafoam_Islands_B3F": 0x470f7, + "Hidden_Item_Vermilion_City": 0x470fe, + "Hidden_Item_Cerulean_City": 0x47105, + "Hidden_Item_Route_4": 0x4710c, "Event_Counter": 0x482d3, "Event_Thirsty_Girl_Lemonade": 0x484f9, "Event_Thirsty_Girl_Soda": 0x4851d, @@ -427,163 +489,443 @@ "Event_Mansion_Lady": 0x4872a, "Badge_Celadon_Gym": 0x48a1b, "Event_Celadon_Gym": 0x48a2f, - "Event_Gambling_Addict": 0x49293, - "Gift_Magikarp": 0x49430, - "Option_Aide_Rt11": 0x4958d, - "Event_Rt11_Oaks_Aide": 0x49591, - "Event_Mourning_Girl": 0x4968b, - "Option_Aide_Rt15": 0x49776, - "Event_Rt_15_Oaks_Aide": 0x4977a, - "Missable_Mt_Moon_1F_Item_1": 0x49c75, - "Missable_Mt_Moon_1F_Item_2": 0x49c7c, - "Missable_Mt_Moon_1F_Item_3": 0x49c83, - "Missable_Mt_Moon_1F_Item_4": 0x49c8a, - "Missable_Mt_Moon_1F_Item_5": 0x49c91, - "Missable_Mt_Moon_1F_Item_6": 0x49c98, - "Dome_Fossil_Text": 0x4a001, - "Event_Dome_Fossil": 0x4a021, - "Helix_Fossil_Text": 0x4a05d, - "Event_Helix_Fossil": 0x4a07d, - "Missable_Mt_Moon_B2F_Item_1": 0x4a166, - "Missable_Mt_Moon_B2F_Item_2": 0x4a16d, - "Missable_Safari_Zone_West_Item_1": 0x4a34f, - "Missable_Safari_Zone_West_Item_2": 0x4a356, - "Missable_Safari_Zone_West_Item_3": 0x4a35d, - "Missable_Safari_Zone_West_Item_4": 0x4a364, - "Event_Safari_Zone_Secret_House": 0x4a469, + "Trainersanity_EVENT_BEAT_CELADON_GYM_TRAINER_0_ITEM": 0x48a75, + "Trainersanity_EVENT_BEAT_CELADON_GYM_TRAINER_1_ITEM": 0x48a83, + "Trainersanity_EVENT_BEAT_CELADON_GYM_TRAINER_2_ITEM": 0x48a91, + "Trainersanity_EVENT_BEAT_CELADON_GYM_TRAINER_3_ITEM": 0x48a9f, + "Trainersanity_EVENT_BEAT_CELADON_GYM_TRAINER_4_ITEM": 0x48aad, + "Trainersanity_EVENT_BEAT_CELADON_GYM_TRAINER_5_ITEM": 0x48abb, + "Trainersanity_EVENT_BEAT_CELADON_GYM_TRAINER_6_ITEM": 0x48ac9, + "Event_Gambling_Addict": 0x492a1, + "Gift_Magikarp": 0x4943e, + "Option_Aide_Rt11": 0x4959b, + "Event_Rt11_Oaks_Aide": 0x4959f, + "Event_Mourning_Girl": 0x49699, + "Option_Aide_Rt15": 0x49784, + "Event_Rt_15_Oaks_Aide": 0x49788, + "Trainersanity_EVENT_BEAT_MT_MOON_1_TRAINER_0_ITEM": 0x49b2e, + "Trainersanity_EVENT_BEAT_MT_MOON_1_TRAINER_1_ITEM": 0x49b3c, + "Trainersanity_EVENT_BEAT_MT_MOON_1_TRAINER_2_ITEM": 0x49b4a, + "Trainersanity_EVENT_BEAT_MT_MOON_1_TRAINER_3_ITEM": 0x49b58, + "Trainersanity_EVENT_BEAT_MT_MOON_1_TRAINER_4_ITEM": 0x49b66, + "Trainersanity_EVENT_BEAT_MT_MOON_1_TRAINER_5_ITEM": 0x49b74, + "Trainersanity_EVENT_BEAT_MT_MOON_1_TRAINER_6_ITEM": 0x49b82, + "Missable_Mt_Moon_1F_Item_1": 0x49c91, + "Missable_Mt_Moon_1F_Item_2": 0x49c98, + "Missable_Mt_Moon_1F_Item_3": 0x49c9f, + "Missable_Mt_Moon_1F_Item_4": 0x49ca6, + "Missable_Mt_Moon_1F_Item_5": 0x49cad, + "Missable_Mt_Moon_1F_Item_6": 0x49cb4, + "Trainersanity_EVENT_BEAT_MT_MOON_3_TRAINER_0_ITEM": 0x49f87, + "Trainersanity_EVENT_BEAT_MT_MOON_3_TRAINER_1_ITEM": 0x49f95, + "Trainersanity_EVENT_BEAT_MT_MOON_3_TRAINER_2_ITEM": 0x49fa3, + "Trainersanity_EVENT_BEAT_MT_MOON_3_TRAINER_3_ITEM": 0x49fb1, + "Dome_Fossil_Text": 0x4a025, + "Event_Dome_Fossil": 0x4a045, + "Helix_Fossil_Text": 0x4a081, + "Event_Helix_Fossil": 0x4a0a1, + "Missable_Mt_Moon_B2F_Item_1": 0x4a18a, + "Missable_Mt_Moon_B2F_Item_2": 0x4a191, + "Missable_Safari_Zone_West_Item_1": 0x4a373, + "Missable_Safari_Zone_West_Item_2": 0x4a37a, + "Missable_Safari_Zone_West_Item_3": 0x4a381, + "Missable_Safari_Zone_West_Item_4": 0x4a388, + "Event_Safari_Zone_Secret_House": 0x4a48d, "Missable_Route_24_Item": 0x506e6, "Missable_Route_25_Item": 0x5080b, - "Starter2_B": 0x50fce, - "Starter3_B": 0x50fd0, - "Starter1_B": 0x50fd2, - "Starter2_A": 0x510f1, - "Starter3_A": 0x510f3, - "Starter1_A": 0x510f5, - "Option_Badge_Goal": 0x51317, - "Event_Nugget_Bridge": 0x5148f, - "Static_Encounter_Moltres": 0x51939, - "Missable_Victory_Road_2F_Item_1": 0x51941, - "Missable_Victory_Road_2F_Item_2": 0x51948, - "Missable_Victory_Road_2F_Item_3": 0x5194f, - "Missable_Victory_Road_2F_Item_4": 0x51956, - "Starter2_L": 0x51c85, - "Starter3_L": 0x51c8d, - "Gift_Lapras": 0x51d83, - "Missable_Silph_Co_7F_Item_1": 0x51f0d, - "Missable_Silph_Co_7F_Item_2": 0x51f14, - "Missable_Pokemon_Mansion_2F_Item": 0x520c9, - "Missable_Pokemon_Mansion_3F_Item_1": 0x522e2, - "Missable_Pokemon_Mansion_3F_Item_2": 0x522e9, - "Missable_Pokemon_Mansion_B1F_Item_1": 0x5248c, - "Missable_Pokemon_Mansion_B1F_Item_2": 0x52493, - "Missable_Pokemon_Mansion_B1F_Item_3": 0x5249a, - "Missable_Pokemon_Mansion_B1F_Item_4": 0x524a1, - "Missable_Pokemon_Mansion_B1F_Item_5": 0x524ae, - "Option_Safari_Zone_Battle_Type": 0x525c3, - "Prize_Mon_A2": 0x5282f, - "Prize_Mon_B2": 0x52830, - "Prize_Mon_C2": 0x52831, - "Prize_Mon_D2": 0x5283a, - "Prize_Mon_E2": 0x5283b, - "Prize_Mon_F2": 0x5283c, - "Prize_Mon_A": 0x52960, - "Prize_Mon_B": 0x52962, - "Prize_Mon_C": 0x52964, - "Prize_Mon_D": 0x52966, - "Prize_Mon_E": 0x52968, - "Prize_Mon_F": 0x5296a, + "Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_0_ITEM": 0x50d47, + "Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_1_ITEM": 0x50d55, + "Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_2_ITEM": 0x50d63, + "Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_3_ITEM": 0x50d71, + "Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_4_ITEM": 0x50d7f, + "Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_5_ITEM": 0x50d8d, + "Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_6_ITEM": 0x50d9b, + "Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_7_ITEM": 0x50da9, + "Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_8_ITEM": 0x50db7, + "Trainersanity_EVENT_BEAT_ROUTE_20_TRAINER_9_ITEM": 0x50dc5, + "Starter2_B": 0x50fe2, + "Starter3_B": 0x50fe4, + "Starter1_B": 0x50fe6, + "Starter2_A": 0x51105, + "Starter3_A": 0x51107, + "Starter1_A": 0x51109, + "Option_Badge_Goal": 0x5132b, + "Trainersanity_EVENT_BEAT_ROUTE_24_TRAINER_0_ITEM": 0x51452, + "Trainersanity_EVENT_BEAT_ROUTE_24_TRAINER_1_ITEM": 0x51460, + "Trainersanity_EVENT_BEAT_ROUTE_24_TRAINER_2_ITEM": 0x5146e, + "Trainersanity_EVENT_BEAT_ROUTE_24_TRAINER_3_ITEM": 0x5147c, + "Trainersanity_EVENT_BEAT_ROUTE_24_TRAINER_4_ITEM": 0x5148a, + "Trainersanity_EVENT_BEAT_ROUTE_24_TRAINER_5_ITEM": 0x51498, + "Event_Nugget_Bridge": 0x514af, + "Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_0_ITEM": 0x51641, + "Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_1_ITEM": 0x5164f, + "Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_2_ITEM": 0x5165d, + "Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_3_ITEM": 0x5166b, + "Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_4_ITEM": 0x51679, + "Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_5_ITEM": 0x51687, + "Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_6_ITEM": 0x51695, + "Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_7_ITEM": 0x516a3, + "Trainersanity_EVENT_BEAT_ROUTE_25_TRAINER_8_ITEM": 0x516b1, + "Trainersanity_EVENT_BEAT_VICTORY_ROAD_2_TRAINER_0_ITEM": 0x5184a, + "Trainersanity_EVENT_BEAT_VICTORY_ROAD_2_TRAINER_1_ITEM": 0x51858, + "Trainersanity_EVENT_BEAT_VICTORY_ROAD_2_TRAINER_2_ITEM": 0x51866, + "Trainersanity_EVENT_BEAT_VICTORY_ROAD_2_TRAINER_3_ITEM": 0x51874, + "Trainersanity_EVENT_BEAT_VICTORY_ROAD_2_TRAINER_4_ITEM": 0x51882, + "Trainersanity_EVENT_BEAT_MOLTRES_ITEM": 0x51890, + "Static_Encounter_Moltres": 0x51977, + "Missable_Victory_Road_2F_Item_1": 0x5197f, + "Missable_Victory_Road_2F_Item_2": 0x51986, + "Missable_Victory_Road_2F_Item_3": 0x5198d, + "Missable_Victory_Road_2F_Item_4": 0x51994, + "Starter2_L": 0x51cc3, + "Starter3_L": 0x51ccb, + "Trainersanity_EVENT_BEAT_SILPH_CO_7F_TRAINER_0_ITEM": 0x51d7e, + "Trainersanity_EVENT_BEAT_SILPH_CO_7F_TRAINER_1_ITEM": 0x51d8c, + "Trainersanity_EVENT_BEAT_SILPH_CO_7F_TRAINER_2_ITEM": 0x51d9a, + "Trainersanity_EVENT_BEAT_SILPH_CO_7F_TRAINER_3_ITEM": 0x51da8, + "Gift_Lapras": 0x51dc9, + "Missable_Silph_Co_7F_Item_1": 0x51f53, + "Missable_Silph_Co_7F_Item_2": 0x51f5a, + "Trainersanity_EVENT_BEAT_MANSION_2_TRAINER_0_ITEM": 0x52080, + "Missable_Pokemon_Mansion_2F_Item": 0x52111, + "Trainersanity_EVENT_BEAT_MANSION_3_TRAINER_0_ITEM": 0x522c1, + "Trainersanity_EVENT_BEAT_MANSION_3_TRAINER_1_ITEM": 0x522cf, + "Missable_Pokemon_Mansion_3F_Item_1": 0x5232e, + "Missable_Pokemon_Mansion_3F_Item_2": 0x52335, + "Trainersanity_EVENT_BEAT_MANSION_4_TRAINER_0_ITEM": 0x52477, + "Trainersanity_EVENT_BEAT_MANSION_4_TRAINER_1_ITEM": 0x52485, + "Missable_Pokemon_Mansion_B1F_Item_1": 0x524dc, + "Missable_Pokemon_Mansion_B1F_Item_2": 0x524e3, + "Missable_Pokemon_Mansion_B1F_Item_3": 0x524ea, + "Missable_Pokemon_Mansion_B1F_Item_4": 0x524f1, + "Missable_Pokemon_Mansion_B1F_Item_5": 0x524fe, + "Option_Safari_Zone_Battle_Type": 0x52613, + "Prize_Mon_A2": 0x5287f, + "Prize_Mon_B2": 0x52880, + "Prize_Mon_C2": 0x52881, + "Prize_Mon_D2": 0x5288a, + "Prize_Mon_E2": 0x5288b, + "Prize_Mon_F2": 0x5288c, + "Prize_Mon_A": 0x529b0, + "Prize_Mon_B": 0x529b2, + "Prize_Mon_C": 0x529b4, + "Prize_Mon_D": 0x529b6, + "Prize_Mon_E": 0x529b8, + "Prize_Mon_F": 0x529ba, + "Start_Inventory": 0x52add, "Missable_Route_2_Item_1": 0x5404a, "Missable_Route_2_Item_2": 0x54051, "Missable_Route_4_Item": 0x543df, "Missable_Route_9_Item": 0x546fd, "Option_EXP_Modifier": 0x552c5, - "Rod_Vermilion_City_Fishing_Guru": 0x560df, - "Rod_Fuchsia_City_Fishing_Brother": 0x561eb, - "Rod_Route12_Fishing_Brother": 0x564ee, + "Trainersanity_EVENT_BEAT_ROUTE_3_TRAINER_0_ITEM": 0x55581, + "Trainersanity_EVENT_BEAT_ROUTE_3_TRAINER_1_ITEM": 0x5558f, + "Trainersanity_EVENT_BEAT_ROUTE_3_TRAINER_2_ITEM": 0x5559d, + "Trainersanity_EVENT_BEAT_ROUTE_3_TRAINER_3_ITEM": 0x555ab, + "Trainersanity_EVENT_BEAT_ROUTE_3_TRAINER_4_ITEM": 0x555b9, + "Trainersanity_EVENT_BEAT_ROUTE_3_TRAINER_5_ITEM": 0x555c7, + "Trainersanity_EVENT_BEAT_ROUTE_3_TRAINER_6_ITEM": 0x555d5, + "Trainersanity_EVENT_BEAT_ROUTE_3_TRAINER_7_ITEM": 0x555e3, + "Trainersanity_EVENT_BEAT_ROUTE_4_TRAINER_0_ITEM": 0x556e9, + "Trainersanity_EVENT_BEAT_ROUTE_9_TRAINER_0_ITEM": 0x55759, + "Trainersanity_EVENT_BEAT_ROUTE_9_TRAINER_1_ITEM": 0x55767, + "Trainersanity_EVENT_BEAT_ROUTE_9_TRAINER_2_ITEM": 0x55775, + "Trainersanity_EVENT_BEAT_ROUTE_9_TRAINER_3_ITEM": 0x55783, + "Trainersanity_EVENT_BEAT_ROUTE_9_TRAINER_4_ITEM": 0x55791, + "Trainersanity_EVENT_BEAT_ROUTE_9_TRAINER_5_ITEM": 0x5579f, + "Trainersanity_EVENT_BEAT_ROUTE_9_TRAINER_6_ITEM": 0x557ad, + "Trainersanity_EVENT_BEAT_ROUTE_9_TRAINER_7_ITEM": 0x557bb, + "Trainersanity_EVENT_BEAT_ROUTE_9_TRAINER_8_ITEM": 0x557c9, + "Trainersanity_EVENT_BEAT_ROUTE_13_TRAINER_0_ITEM": 0x558d3, + "Trainersanity_EVENT_BEAT_ROUTE_13_TRAINER_1_ITEM": 0x558e1, + "Trainersanity_EVENT_BEAT_ROUTE_13_TRAINER_2_ITEM": 0x558ef, + "Trainersanity_EVENT_BEAT_ROUTE_13_TRAINER_3_ITEM": 0x558fd, + "Trainersanity_EVENT_BEAT_ROUTE_13_TRAINER_4_ITEM": 0x5590b, + "Trainersanity_EVENT_BEAT_ROUTE_13_TRAINER_5_ITEM": 0x55919, + "Trainersanity_EVENT_BEAT_ROUTE_13_TRAINER_6_ITEM": 0x55927, + "Trainersanity_EVENT_BEAT_ROUTE_13_TRAINER_7_ITEM": 0x55935, + "Trainersanity_EVENT_BEAT_ROUTE_13_TRAINER_8_ITEM": 0x55943, + "Trainersanity_EVENT_BEAT_ROUTE_13_TRAINER_9_ITEM": 0x55951, + "Trainersanity_EVENT_BEAT_ROUTE_14_TRAINER_0_ITEM": 0x55a98, + "Trainersanity_EVENT_BEAT_ROUTE_14_TRAINER_1_ITEM": 0x55aa6, + "Trainersanity_EVENT_BEAT_ROUTE_14_TRAINER_2_ITEM": 0x55ab4, + "Trainersanity_EVENT_BEAT_ROUTE_14_TRAINER_3_ITEM": 0x55ac2, + "Trainersanity_EVENT_BEAT_ROUTE_14_TRAINER_4_ITEM": 0x55ad0, + "Trainersanity_EVENT_BEAT_ROUTE_14_TRAINER_5_ITEM": 0x55ade, + "Trainersanity_EVENT_BEAT_ROUTE_14_TRAINER_6_ITEM": 0x55aec, + "Trainersanity_EVENT_BEAT_ROUTE_14_TRAINER_7_ITEM": 0x55afa, + "Trainersanity_EVENT_BEAT_ROUTE_14_TRAINER_8_ITEM": 0x55b08, + "Trainersanity_EVENT_BEAT_ROUTE_14_TRAINER_9_ITEM": 0x55b16, + "Trainersanity_EVENT_BEAT_ROUTE_17_TRAINER_0_ITEM": 0x55c5d, + "Trainersanity_EVENT_BEAT_ROUTE_17_TRAINER_1_ITEM": 0x55c6b, + "Trainersanity_EVENT_BEAT_ROUTE_17_TRAINER_2_ITEM": 0x55c79, + "Trainersanity_EVENT_BEAT_ROUTE_17_TRAINER_3_ITEM": 0x55c87, + "Trainersanity_EVENT_BEAT_ROUTE_17_TRAINER_4_ITEM": 0x55c95, + "Trainersanity_EVENT_BEAT_ROUTE_17_TRAINER_5_ITEM": 0x55ca3, + "Trainersanity_EVENT_BEAT_ROUTE_17_TRAINER_6_ITEM": 0x55cb1, + "Trainersanity_EVENT_BEAT_ROUTE_17_TRAINER_7_ITEM": 0x55cbf, + "Trainersanity_EVENT_BEAT_ROUTE_17_TRAINER_8_ITEM": 0x55ccd, + "Trainersanity_EVENT_BEAT_ROUTE_17_TRAINER_9_ITEM": 0x55cdb, + "Trainersanity_EVENT_BEAT_ROUTE_19_TRAINER_0_ITEM": 0x55e31, + "Trainersanity_EVENT_BEAT_ROUTE_19_TRAINER_1_ITEM": 0x55e3f, + "Trainersanity_EVENT_BEAT_ROUTE_19_TRAINER_2_ITEM": 0x55e4d, + "Trainersanity_EVENT_BEAT_ROUTE_19_TRAINER_3_ITEM": 0x55e5b, + "Trainersanity_EVENT_BEAT_ROUTE_19_TRAINER_4_ITEM": 0x55e69, + "Trainersanity_EVENT_BEAT_ROUTE_19_TRAINER_5_ITEM": 0x55e77, + "Trainersanity_EVENT_BEAT_ROUTE_19_TRAINER_6_ITEM": 0x55e85, + "Trainersanity_EVENT_BEAT_ROUTE_19_TRAINER_7_ITEM": 0x55e93, + "Trainersanity_EVENT_BEAT_ROUTE_19_TRAINER_8_ITEM": 0x55ea1, + "Trainersanity_EVENT_BEAT_ROUTE_19_TRAINER_9_ITEM": 0x55eaf, + "Trainersanity_EVENT_BEAT_ROUTE_21_TRAINER_0_ITEM": 0x55fe8, + "Trainersanity_EVENT_BEAT_ROUTE_21_TRAINER_1_ITEM": 0x55ff6, + "Trainersanity_EVENT_BEAT_ROUTE_21_TRAINER_2_ITEM": 0x56004, + "Trainersanity_EVENT_BEAT_ROUTE_21_TRAINER_3_ITEM": 0x56012, + "Trainersanity_EVENT_BEAT_ROUTE_21_TRAINER_4_ITEM": 0x56020, + "Trainersanity_EVENT_BEAT_ROUTE_21_TRAINER_5_ITEM": 0x5602e, + "Trainersanity_EVENT_BEAT_ROUTE_21_TRAINER_6_ITEM": 0x5603c, + "Trainersanity_EVENT_BEAT_ROUTE_21_TRAINER_7_ITEM": 0x5604a, + "Trainersanity_EVENT_BEAT_ROUTE_21_TRAINER_8_ITEM": 0x56058, + "Rod_Vermilion_City_Fishing_Guru": 0x56165, + "Shop6": 0x561e8, + "Rod_Fuchsia_City_Fishing_Brother": 0x562a7, + "Rod_Route12_Fishing_Brother": 0x565aa, + "Trainersanity_EVENT_BEAT_SILPH_CO_8F_TRAINER_0_ITEM": 0x5669f, + "Trainersanity_EVENT_BEAT_SILPH_CO_8F_TRAINER_1_ITEM": 0x566ad, + "Trainersanity_EVENT_BEAT_SILPH_CO_8F_TRAINER_2_ITEM": 0x566bb, "Missable_Route_12_Item_1": 0x58704, "Missable_Route_12_Item_2": 0x5870b, "Missable_Route_15_Item": 0x589c7, "Ghost_Battle6": 0x58df0, - "Static_Encounter_Snorlax_A": 0x5969b, - "Static_Encounter_Snorlax_B": 0x599db, - "Event_Pokemon_Fan_Club": 0x59c8b, - "Event_Scared_Woman": 0x59e1f, - "Missable_Silph_Co_3F_Item": 0x5a0cb, - "Missable_Silph_Co_10F_Item_1": 0x5a281, - "Missable_Silph_Co_10F_Item_2": 0x5a288, - "Missable_Silph_Co_10F_Item_3": 0x5a28f, - "Guard_Drink_List": 0x5a600, + "Trainersanity_EVENT_BEAT_ROUTE_6_TRAINER_0_ITEM": 0x59106, + "Trainersanity_EVENT_BEAT_ROUTE_6_TRAINER_1_ITEM": 0x59114, + "Trainersanity_EVENT_BEAT_ROUTE_6_TRAINER_2_ITEM": 0x59122, + "Trainersanity_EVENT_BEAT_ROUTE_6_TRAINER_3_ITEM": 0x59130, + "Trainersanity_EVENT_BEAT_ROUTE_6_TRAINER_4_ITEM": 0x5913e, + "Trainersanity_EVENT_BEAT_ROUTE_6_TRAINER_5_ITEM": 0x5914c, + "Trainersanity_EVENT_BEAT_ROUTE_8_TRAINER_0_ITEM": 0x5921e, + "Trainersanity_EVENT_BEAT_ROUTE_8_TRAINER_1_ITEM": 0x5922c, + "Trainersanity_EVENT_BEAT_ROUTE_8_TRAINER_2_ITEM": 0x5923a, + "Trainersanity_EVENT_BEAT_ROUTE_8_TRAINER_3_ITEM": 0x59248, + "Trainersanity_EVENT_BEAT_ROUTE_8_TRAINER_4_ITEM": 0x59256, + "Trainersanity_EVENT_BEAT_ROUTE_8_TRAINER_5_ITEM": 0x59264, + "Trainersanity_EVENT_BEAT_ROUTE_8_TRAINER_6_ITEM": 0x59272, + "Trainersanity_EVENT_BEAT_ROUTE_8_TRAINER_7_ITEM": 0x59280, + "Trainersanity_EVENT_BEAT_ROUTE_8_TRAINER_8_ITEM": 0x5928e, + "Trainersanity_EVENT_BEAT_ROUTE_10_TRAINER_0_ITEM": 0x59406, + "Trainersanity_EVENT_BEAT_ROUTE_10_TRAINER_1_ITEM": 0x59414, + "Trainersanity_EVENT_BEAT_ROUTE_10_TRAINER_2_ITEM": 0x59422, + "Trainersanity_EVENT_BEAT_ROUTE_10_TRAINER_3_ITEM": 0x59430, + "Trainersanity_EVENT_BEAT_ROUTE_10_TRAINER_4_ITEM": 0x5943e, + "Trainersanity_EVENT_BEAT_ROUTE_10_TRAINER_5_ITEM": 0x5944c, + "Trainersanity_EVENT_BEAT_ROUTE_11_TRAINER_0_ITEM": 0x59533, + "Trainersanity_EVENT_BEAT_ROUTE_11_TRAINER_1_ITEM": 0x59541, + "Trainersanity_EVENT_BEAT_ROUTE_11_TRAINER_2_ITEM": 0x5954f, + "Trainersanity_EVENT_BEAT_ROUTE_11_TRAINER_3_ITEM": 0x5955d, + "Trainersanity_EVENT_BEAT_ROUTE_11_TRAINER_4_ITEM": 0x5956b, + "Trainersanity_EVENT_BEAT_ROUTE_11_TRAINER_5_ITEM": 0x59579, + "Trainersanity_EVENT_BEAT_ROUTE_11_TRAINER_6_ITEM": 0x59587, + "Trainersanity_EVENT_BEAT_ROUTE_11_TRAINER_7_ITEM": 0x59595, + "Trainersanity_EVENT_BEAT_ROUTE_11_TRAINER_8_ITEM": 0x595a3, + "Trainersanity_EVENT_BEAT_ROUTE_11_TRAINER_9_ITEM": 0x595b1, + "Static_Encounter_Snorlax_A": 0x596ef, + "Trainersanity_EVENT_BEAT_ROUTE_12_TRAINER_0_ITEM": 0x5975d, + "Trainersanity_EVENT_BEAT_ROUTE_12_TRAINER_1_ITEM": 0x5976b, + "Trainersanity_EVENT_BEAT_ROUTE_12_TRAINER_2_ITEM": 0x59779, + "Trainersanity_EVENT_BEAT_ROUTE_12_TRAINER_3_ITEM": 0x59787, + "Trainersanity_EVENT_BEAT_ROUTE_12_TRAINER_4_ITEM": 0x59795, + "Trainersanity_EVENT_BEAT_ROUTE_12_TRAINER_5_ITEM": 0x597a3, + "Trainersanity_EVENT_BEAT_ROUTE_12_TRAINER_6_ITEM": 0x597b1, + "Trainersanity_EVENT_BEAT_ROUTE_15_TRAINER_0_ITEM": 0x598b9, + "Trainersanity_EVENT_BEAT_ROUTE_15_TRAINER_1_ITEM": 0x598c7, + "Trainersanity_EVENT_BEAT_ROUTE_15_TRAINER_2_ITEM": 0x598d5, + "Trainersanity_EVENT_BEAT_ROUTE_15_TRAINER_3_ITEM": 0x598e3, + "Trainersanity_EVENT_BEAT_ROUTE_15_TRAINER_4_ITEM": 0x598f1, + "Trainersanity_EVENT_BEAT_ROUTE_15_TRAINER_5_ITEM": 0x598ff, + "Trainersanity_EVENT_BEAT_ROUTE_15_TRAINER_6_ITEM": 0x5990d, + "Trainersanity_EVENT_BEAT_ROUTE_15_TRAINER_7_ITEM": 0x5991b, + "Trainersanity_EVENT_BEAT_ROUTE_15_TRAINER_8_ITEM": 0x59929, + "Trainersanity_EVENT_BEAT_ROUTE_15_TRAINER_9_ITEM": 0x59937, + "Static_Encounter_Snorlax_B": 0x59a51, + "Trainersanity_EVENT_BEAT_ROUTE_16_TRAINER_0_ITEM": 0x59abd, + "Trainersanity_EVENT_BEAT_ROUTE_16_TRAINER_1_ITEM": 0x59acb, + "Trainersanity_EVENT_BEAT_ROUTE_16_TRAINER_2_ITEM": 0x59ad9, + "Trainersanity_EVENT_BEAT_ROUTE_16_TRAINER_3_ITEM": 0x59ae7, + "Trainersanity_EVENT_BEAT_ROUTE_16_TRAINER_4_ITEM": 0x59af5, + "Trainersanity_EVENT_BEAT_ROUTE_16_TRAINER_5_ITEM": 0x59b03, + "Trainersanity_EVENT_BEAT_ROUTE_18_TRAINER_0_ITEM": 0x59be4, + "Trainersanity_EVENT_BEAT_ROUTE_18_TRAINER_1_ITEM": 0x59bf2, + "Trainersanity_EVENT_BEAT_ROUTE_18_TRAINER_2_ITEM": 0x59c00, + "Event_Pokemon_Fan_Club": 0x59d13, + "Trainersanity_EVENT_BEAT_SILPH_CO_2F_TRAINER_0_ITEM": 0x59e73, + "Trainersanity_EVENT_BEAT_SILPH_CO_2F_TRAINER_1_ITEM": 0x59e81, + "Trainersanity_EVENT_BEAT_SILPH_CO_2F_TRAINER_2_ITEM": 0x59e8f, + "Trainersanity_EVENT_BEAT_SILPH_CO_2F_TRAINER_3_ITEM": 0x59e9d, + "Event_Scared_Woman": 0x59eaf, + "Trainersanity_EVENT_BEAT_SILPH_CO_3F_TRAINER_0_ITEM": 0x5a0b7, + "Trainersanity_EVENT_BEAT_SILPH_CO_3F_TRAINER_1_ITEM": 0x5a0c5, + "Missable_Silph_Co_3F_Item": 0x5a15f, + "Trainersanity_EVENT_BEAT_SILPH_CO_10F_TRAINER_0_ITEM": 0x5a281, + "Trainersanity_EVENT_BEAT_SILPH_CO_10F_TRAINER_1_ITEM": 0x5a28f, + "Missable_Silph_Co_10F_Item_1": 0x5a319, + "Missable_Silph_Co_10F_Item_2": 0x5a320, + "Missable_Silph_Co_10F_Item_3": 0x5a327, + "Trainersanity_EVENT_BEAT_LANCES_ROOM_TRAINER_0_ITEM": 0x5a48a, + "Guard_Drink_List": 0x5a69f, "Event_Museum": 0x5c266, "Badge_Pewter_Gym": 0x5c3ed, "Event_Pewter_Gym": 0x5c401, - "Badge_Cerulean_Gym": 0x5c716, - "Event_Cerulean_Gym": 0x5c72a, - "Badge_Vermilion_Gym": 0x5caba, - "Event_Vermillion_Gym": 0x5cace, - "Event_Copycat": 0x5cca9, - "Gift_Hitmonlee": 0x5cf1a, - "Gift_Hitmonchan": 0x5cf62, - "Badge_Saffron_Gym": 0x5d079, - "Event_Saffron_Gym": 0x5d08d, - "Option_Aide_Rt2": 0x5d5f2, - "Event_Route_2_Oaks_Aide": 0x5d5f6, - "Missable_Victory_Road_1F_Item_1": 0x5dae6, - "Missable_Victory_Road_1F_Item_2": 0x5daed, + "Trainersanity_EVENT_BEAT_PEWTER_GYM_TRAINER_0_ITEM": 0x5c447, + "Badge_Cerulean_Gym": 0x5c718, + "Event_Cerulean_Gym": 0x5c72c, + "Trainersanity_EVENT_BEAT_CERULEAN_GYM_TRAINER_0_ITEM": 0x5c76a, + "Trainersanity_EVENT_BEAT_CERULEAN_GYM_TRAINER_1_ITEM": 0x5c778, + "Shop3": 0x5c8b0, + "Shop5": 0x5c98d, + "Shop4": 0x5ca4e, + "Badge_Vermilion_Gym": 0x5cb39, + "Event_Vermillion_Gym": 0x5cb4d, + "Trainersanity_EVENT_BEAT_VERMILION_GYM_TRAINER_0_ITEM": 0x5cb8d, + "Trainersanity_EVENT_BEAT_VERMILION_GYM_TRAINER_1_ITEM": 0x5cb9b, + "Trainersanity_EVENT_BEAT_VERMILION_GYM_TRAINER_2_ITEM": 0x5cba9, + "Event_Copycat": 0x5cd2e, + "Trainersanity_EVENT_BEAT_FIGHTING_DOJO_TRAINER_0_ITEM": 0x5cea8, + "Trainersanity_EVENT_BEAT_FIGHTING_DOJO_TRAINER_1_ITEM": 0x5ceb6, + "Trainersanity_EVENT_BEAT_FIGHTING_DOJO_TRAINER_2_ITEM": 0x5cec4, + "Trainersanity_EVENT_BEAT_FIGHTING_DOJO_TRAINER_3_ITEM": 0x5ced2, + "Gift_Hitmonlee": 0x5cfa7, + "Gift_Hitmonchan": 0x5cfef, + "Badge_Saffron_Gym": 0x5d106, + "Event_Saffron_Gym": 0x5d11a, + "Trainersanity_EVENT_BEAT_SAFFRON_GYM_TRAINER_0_ITEM": 0x5d162, + "Trainersanity_EVENT_BEAT_SAFFRON_GYM_TRAINER_1_ITEM": 0x5d170, + "Trainersanity_EVENT_BEAT_SAFFRON_GYM_TRAINER_2_ITEM": 0x5d17e, + "Trainersanity_EVENT_BEAT_SAFFRON_GYM_TRAINER_3_ITEM": 0x5d18c, + "Trainersanity_EVENT_BEAT_SAFFRON_GYM_TRAINER_4_ITEM": 0x5d19a, + "Trainersanity_EVENT_BEAT_SAFFRON_GYM_TRAINER_5_ITEM": 0x5d1a8, + "Trainersanity_EVENT_BEAT_SAFFRON_GYM_TRAINER_6_ITEM": 0x5d1b6, + "Shop9": 0x5d4b8, + "Option_Aide_Rt2": 0x5d6b0, + "Event_Route_2_Oaks_Aide": 0x5d6b4, + "Trainersanity_EVENT_BEAT_SILPH_CO_9F_TRAINER_0_ITEM": 0x5d966, + "Trainersanity_EVENT_BEAT_SILPH_CO_9F_TRAINER_1_ITEM": 0x5d974, + "Trainersanity_EVENT_BEAT_SILPH_CO_9F_TRAINER_2_ITEM": 0x5d982, + "Trainersanity_EVENT_BEAT_VICTORY_ROAD_1_TRAINER_0_ITEM": 0x5db46, + "Trainersanity_EVENT_BEAT_VICTORY_ROAD_1_TRAINER_1_ITEM": 0x5db54, + "Missable_Victory_Road_1F_Item_1": 0x5dbae, + "Missable_Victory_Road_1F_Item_2": 0x5dbb5, "Starter2_J": 0x6060e, "Starter3_J": 0x60616, - "Missable_Pokemon_Tower_3F_Item": 0x60787, - "Missable_Pokemon_Tower_4F_Item_1": 0x608b5, - "Missable_Pokemon_Tower_4F_Item_2": 0x608bc, - "Missable_Pokemon_Tower_4F_Item_3": 0x608c3, - "Missable_Pokemon_Tower_5F_Item": 0x60a80, - "Ghost_Battle1": 0x60b33, - "Ghost_Battle2": 0x60c0a, - "Missable_Pokemon_Tower_6F_Item_1": 0x60c85, - "Missable_Pokemon_Tower_6F_Item_2": 0x60c8c, - "Gift_Aerodactyl": 0x61064, - "Gift_Omanyte": 0x61068, - "Gift_Kabuto": 0x6106c, - "Missable_Viridian_Forest_Item_1": 0x6122c, - "Missable_Viridian_Forest_Item_2": 0x61233, - "Missable_Viridian_Forest_Item_3": 0x6123a, - "Starter2_M": 0x61450, - "Starter3_M": 0x61458, - "Event_SS_Anne_Captain": 0x618c3, - "Missable_SS_Anne_1F_Item": 0x61ac0, - "Missable_SS_Anne_2F_Item_1": 0x61ced, - "Missable_SS_Anne_2F_Item_2": 0x61d00, - "Missable_SS_Anne_B1F_Item_1": 0x61ee3, - "Missable_SS_Anne_B1F_Item_2": 0x61eea, - "Missable_SS_Anne_B1F_Item_3": 0x61ef1, - "Event_Silph_Co_President": 0x622ed, + "Trainersanity_EVENT_BEAT_POKEMONTOWER_3_TRAINER_0_ITEM": 0x606fa, + "Trainersanity_EVENT_BEAT_POKEMONTOWER_3_TRAINER_1_ITEM": 0x60708, + "Trainersanity_EVENT_BEAT_POKEMONTOWER_3_TRAINER_2_ITEM": 0x60716, + "Missable_Pokemon_Tower_3F_Item": 0x6078d, + "Trainersanity_EVENT_BEAT_POKEMONTOWER_4_TRAINER_0_ITEM": 0x6082e, + "Trainersanity_EVENT_BEAT_POKEMONTOWER_4_TRAINER_1_ITEM": 0x6083c, + "Trainersanity_EVENT_BEAT_POKEMONTOWER_4_TRAINER_2_ITEM": 0x6084a, + "Missable_Pokemon_Tower_4F_Item_1": 0x608c1, + "Missable_Pokemon_Tower_4F_Item_2": 0x608c8, + "Missable_Pokemon_Tower_4F_Item_3": 0x608cf, + "Trainersanity_EVENT_BEAT_POKEMONTOWER_5_TRAINER_0_ITEM": 0x609c2, + "Trainersanity_EVENT_BEAT_POKEMONTOWER_5_TRAINER_1_ITEM": 0x609d0, + "Trainersanity_EVENT_BEAT_POKEMONTOWER_5_TRAINER_2_ITEM": 0x609de, + "Trainersanity_EVENT_BEAT_POKEMONTOWER_5_TRAINER_3_ITEM": 0x609ec, + "Missable_Pokemon_Tower_5F_Item": 0x60a94, + "Option_Trainersanity2": 0x60b2c, + "Ghost_Battle1": 0x60b7f, + "Trainersanity_EVENT_BEAT_POKEMONTOWER_6_TRAINER_0_ITEM": 0x60c18, + "Trainersanity_EVENT_BEAT_POKEMONTOWER_6_TRAINER_1_ITEM": 0x60c26, + "Trainersanity_EVENT_BEAT_POKEMONTOWER_6_TRAINER_2_ITEM": 0x60c34, + "Ghost_Battle2": 0x60c5c, + "Missable_Pokemon_Tower_6F_Item_1": 0x60cd7, + "Missable_Pokemon_Tower_6F_Item_2": 0x60cde, + "Trainersanity_EVENT_BEAT_POKEMONTOWER_7_TRAINER_0_ITEM": 0x60ea6, + "Trainersanity_EVENT_BEAT_POKEMONTOWER_7_TRAINER_1_ITEM": 0x60eb4, + "Trainersanity_EVENT_BEAT_POKEMONTOWER_7_TRAINER_2_ITEM": 0x60ec2, + "Gift_Aerodactyl": 0x610bc, + "Gift_Omanyte": 0x610c0, + "Gift_Kabuto": 0x610c4, + "Trainersanity_EVENT_BEAT_VIRIDIAN_FOREST_TRAINER_0_ITEM": 0x611a7, + "Trainersanity_EVENT_BEAT_VIRIDIAN_FOREST_TRAINER_1_ITEM": 0x611b5, + "Trainersanity_EVENT_BEAT_VIRIDIAN_FOREST_TRAINER_2_ITEM": 0x611c3, + "Missable_Viridian_Forest_Item_1": 0x6128a, + "Missable_Viridian_Forest_Item_2": 0x61291, + "Missable_Viridian_Forest_Item_3": 0x61298, + "Starter2_M": 0x614ae, + "Starter3_M": 0x614b6, + "Trainersanity_EVENT_BEAT_SS_ANNE_5_TRAINER_0_ITEM": 0x6173c, + "Trainersanity_EVENT_BEAT_SS_ANNE_5_TRAINER_1_ITEM": 0x6174a, + "Event_SS_Anne_Captain": 0x61925, + "Trainersanity_EVENT_BEAT_SS_ANNE_8_TRAINER_0_ITEM": 0x61a14, + "Trainersanity_EVENT_BEAT_SS_ANNE_8_TRAINER_1_ITEM": 0x61a22, + "Trainersanity_EVENT_BEAT_SS_ANNE_8_TRAINER_2_ITEM": 0x61a30, + "Trainersanity_EVENT_BEAT_SS_ANNE_8_TRAINER_3_ITEM": 0x61a3e, + "Missable_SS_Anne_1F_Item": 0x61b2a, + "Trainersanity_EVENT_BEAT_SS_ANNE_9_TRAINER_0_ITEM": 0x61bfb, + "Trainersanity_EVENT_BEAT_SS_ANNE_9_TRAINER_1_ITEM": 0x61c09, + "Trainersanity_EVENT_BEAT_SS_ANNE_9_TRAINER_2_ITEM": 0x61c17, + "Trainersanity_EVENT_BEAT_SS_ANNE_9_TRAINER_3_ITEM": 0x61c25, + "Missable_SS_Anne_2F_Item_1": 0x61d5f, + "Missable_SS_Anne_2F_Item_2": 0x61d72, + "Trainersanity_EVENT_BEAT_SS_ANNE_10_TRAINER_0_ITEM": 0x61e03, + "Trainersanity_EVENT_BEAT_SS_ANNE_10_TRAINER_1_ITEM": 0x61e11, + "Trainersanity_EVENT_BEAT_SS_ANNE_10_TRAINER_2_ITEM": 0x61e1f, + "Trainersanity_EVENT_BEAT_SS_ANNE_10_TRAINER_3_ITEM": 0x61e2d, + "Trainersanity_EVENT_BEAT_SS_ANNE_10_TRAINER_4_ITEM": 0x61e3b, + "Trainersanity_EVENT_BEAT_SS_ANNE_10_TRAINER_5_ITEM": 0x61e49, + "Missable_SS_Anne_B1F_Item_1": 0x61f61, + "Missable_SS_Anne_B1F_Item_2": 0x61f68, + "Missable_SS_Anne_B1F_Item_3": 0x61f6f, + "Trainersanity_EVENT_BEAT_SILPH_CO_11F_TRAINER_0_ITEM": 0x62330, + "Trainersanity_EVENT_BEAT_SILPH_CO_11F_TRAINER_1_ITEM": 0x6233e, + "Event_Silph_Co_President": 0x62351, "Ghost_Battle4": 0x708e1, - "Badge_Viridian_Gym": 0x749ca, - "Event_Viridian_Gym": 0x749de, - "Missable_Viridian_Gym_Item": 0x74c63, - "Missable_Cerulean_Cave_1F_Item_1": 0x74d68, - "Missable_Cerulean_Cave_1F_Item_2": 0x74d6f, - "Missable_Cerulean_Cave_1F_Item_3": 0x74d76, - "Event_Warden": 0x7512a, - "Missable_Wardens_House_Item": 0x751b7, - "Badge_Fuchsia_Gym": 0x755cd, - "Event_Fuschia_Gym": 0x755e1, - "Badge_Cinnabar_Gym": 0x75995, - "Event_Cinnabar_Gym": 0x759a9, - "Event_Lab_Scientist": 0x75dd6, - "Fossils_Needed_For_Second_Item": 0x75ea3, - "Event_Dome_Fossil_B": 0x75f20, - "Event_Helix_Fossil_B": 0x75f40, - "Starter2_N": 0x76169, - "Starter3_N": 0x76171, - "Option_Itemfinder": 0x76864, + "Badge_Viridian_Gym": 0x749f7, + "Event_Viridian_Gym": 0x74a0b, + "Trainersanity_EVENT_BEAT_VIRIDIAN_GYM_TRAINER_0_ITEM": 0x74a66, + "Trainersanity_EVENT_BEAT_VIRIDIAN_GYM_TRAINER_1_ITEM": 0x74a74, + "Trainersanity_EVENT_BEAT_VIRIDIAN_GYM_TRAINER_2_ITEM": 0x74a82, + "Trainersanity_EVENT_BEAT_VIRIDIAN_GYM_TRAINER_3_ITEM": 0x74a90, + "Trainersanity_EVENT_BEAT_VIRIDIAN_GYM_TRAINER_4_ITEM": 0x74a9e, + "Trainersanity_EVENT_BEAT_VIRIDIAN_GYM_TRAINER_5_ITEM": 0x74aac, + "Trainersanity_EVENT_BEAT_VIRIDIAN_GYM_TRAINER_6_ITEM": 0x74aba, + "Trainersanity_EVENT_BEAT_VIRIDIAN_GYM_TRAINER_7_ITEM": 0x74ac8, + "Missable_Viridian_Gym_Item": 0x74ca0, + "Shop2": 0x74d3c, + "Missable_Cerulean_Cave_1F_Item_1": 0x74dcd, + "Missable_Cerulean_Cave_1F_Item_2": 0x74dd4, + "Missable_Cerulean_Cave_1F_Item_3": 0x74ddb, + "Event_Warden": 0x7518f, + "Missable_Wardens_House_Item": 0x7521c, + "Badge_Fuchsia_Gym": 0x75632, + "Event_Fuschia_Gym": 0x75646, + "Trainersanity_EVENT_BEAT_FUCHSIA_GYM_TRAINER_0_ITEM": 0x7568c, + "Trainersanity_EVENT_BEAT_FUCHSIA_GYM_TRAINER_1_ITEM": 0x7569a, + "Trainersanity_EVENT_BEAT_FUCHSIA_GYM_TRAINER_2_ITEM": 0x756a8, + "Trainersanity_EVENT_BEAT_FUCHSIA_GYM_TRAINER_3_ITEM": 0x756b6, + "Trainersanity_EVENT_BEAT_FUCHSIA_GYM_TRAINER_4_ITEM": 0x756c4, + "Trainersanity_EVENT_BEAT_FUCHSIA_GYM_TRAINER_5_ITEM": 0x756d2, + "Badge_Cinnabar_Gym": 0x75a06, + "Event_Cinnabar_Gym": 0x75a1a, + "Event_Lab_Scientist": 0x75e43, + "Fossils_Needed_For_Second_Item": 0x75f10, + "Event_Dome_Fossil_B": 0x75f8d, + "Event_Helix_Fossil_B": 0x75fad, + "Shop8": 0x760cb, + "Starter2_N": 0x761fe, + "Starter3_N": 0x76206, + "Trainersanity_EVENT_BEAT_LORELEIS_ROOM_TRAINER_0_ITEM": 0x764ce, + "Trainersanity_EVENT_BEAT_BRUNOS_ROOM_TRAINER_0_ITEM": 0x76627, + "Trainersanity_EVENT_BEAT_AGATHAS_ROOM_TRAINER_0_ITEM": 0x76786, + "Option_Itemfinder": 0x768ff, + "Text_Magikarp_Salesman": 0x8a7fe, "Text_Badges_Needed": 0x92304, - "Badge_Text_Boulder_Badge": 0x990b3, - "Badge_Text_Cascade_Badge": 0x990cb, - "Badge_Text_Thunder_Badge": 0x99111, - "Badge_Text_Rainbow_Badge": 0x9912e, - "Badge_Text_Soul_Badge": 0x99177, - "Badge_Text_Marsh_Badge": 0x9918c, - "Badge_Text_Volcano_Badge": 0x991d6, - "Badge_Text_Earth_Badge": 0x991f3, + "Badge_Text_Boulder_Badge": 0x99010, + "Badge_Text_Cascade_Badge": 0x99028, + "Badge_Text_Thunder_Badge": 0x9906e, + "Badge_Text_Rainbow_Badge": 0x9908b, + "Badge_Text_Soul_Badge": 0x990d4, + "Badge_Text_Marsh_Badge": 0x990e9, + "Badge_Text_Volcano_Badge": 0x99133, + "Badge_Text_Earth_Badge": 0x99150, "Text_Badges_Needed_Viridian_Gym": 0xa49f2, } diff --git a/worlds/pokemon_rb/rules.py b/worlds/pokemon_rb/rules.py index 6a8399bd241e..e24eaf5e2ea7 100644 --- a/worlds/pokemon_rb/rules.py +++ b/worlds/pokemon_rb/rules.py @@ -3,25 +3,27 @@ def set_rules(world, player): add_item_rule(world.get_location("Pallet Town - Player's PC", player), - lambda i: i.player == player and "Badge" not in i.name) + lambda i: i.player == player and "Badge" not in i.name and "Trap" not in i.name and + i.name != "Pokedex") access_rules = { "Pallet Town - Rival's Sister": lambda state: state.has("Oak's Parcel", player), "Pallet Town - Oak's Post-Route-22-Rival Gift": lambda state: state.has("Oak's Parcel", player), "Viridian City - Sleepy Guy": lambda state: state.pokemon_rb_can_cut(player) or state.pokemon_rb_can_surf(player), - "Route 2 - Oak's Aide": lambda state: state.pokemon_rb_has_pokemon(state.multiworld.oaks_aide_rt_2[player].value + 5, player), + "Route 2 - Oak's Aide": lambda state: state.pokemon_rb_oaks_aide(state.multiworld.oaks_aide_rt_2[player].value + 5, player), "Pewter City - Museum": lambda state: state.pokemon_rb_can_cut(player), "Cerulean City - Bicycle Shop": lambda state: state.has("Bike Voucher", player), "Lavender Town - Mr. Fuji": lambda state: state.has("Fuji Saved", player), "Vermilion Gym - Lt. Surge 1": lambda state: state.pokemon_rb_can_cut(player or state.pokemon_rb_can_surf(player)), "Vermilion Gym - Lt. Surge 2": lambda state: state.pokemon_rb_can_cut(player or state.pokemon_rb_can_surf(player)), - "Route 11 - Oak's Aide": lambda state: state.pokemon_rb_has_pokemon(state.multiworld.oaks_aide_rt_11[player].value + 5, player), + "Route 11 - Oak's Aide": lambda state: state.pokemon_rb_oaks_aide(state.multiworld.oaks_aide_rt_11[player].value + 5, player), "Celadon City - Stranded Man": lambda state: state.pokemon_rb_can_surf(player), "Silph Co 11F - Silph Co President": lambda state: state.has("Card Key", player), "Fuchsia City - Safari Zone Warden": lambda state: state.has("Gold Teeth", player), "Route 12 - Island Item": lambda state: state.pokemon_rb_can_surf(player), "Route 12 - Item Behind Cuttable Tree": lambda state: state.pokemon_rb_can_cut(player), + "Route 15 - Oak's Aide": lambda state: state.pokemon_rb_oaks_aide(state.multiworld.oaks_aide_rt_15[player].value + 5, player), "Route 15 - Item": lambda state: state.pokemon_rb_can_cut(player), "Route 25 - Item": lambda state: state.pokemon_rb_can_cut(player), "Fuchsia City - Warden's House Item": lambda state: state.pokemon_rb_can_strength(player), @@ -85,10 +87,23 @@ def set_rules(world, player): "Route 12 - Sleeping Pokemon": lambda state: state.has("Poke Flute", player), "Route 16 - Sleeping Pokemon": lambda state: state.has("Poke Flute", player), "Seafoam Islands B4F - Legendary Pokemon": lambda state: state.pokemon_rb_can_strength(player), - "Vermilion City - Legendary Pokemon": lambda state: state.pokemon_rb_can_surf(player) and state.has("S.S. Ticket", player) - } + "Vermilion City - Legendary Pokemon": lambda state: state.pokemon_rb_can_surf(player) and state.has("S.S. Ticket", player), + + # Pokédex check + "Pallet Town - Oak's Parcel Reward": lambda state: state.has("Oak's Parcel", player), + + # trainers + "Route 4 - Cooltrainer F": lambda state: state.pokemon_rb_can_surf(player), + "Route 15 - Jr. Trainer F 1": lambda state: state.pokemon_rb_can_cut(player), + "Silph Co 11F - Rocket 2 (Card Key)": lambda state: state.has("Card Key", player), + "Silph Co 9F - Rocket 2 (Card Key)": lambda state: state.has("Card Key", player), + "Silph Co 3F - Scientist (Card Key)": lambda state: state.has("Card Key", player), + "Route 10 North - Pokemaniac": lambda state: state.pokemon_rb_can_surf(player), + "Rocket Hideout B1F - Rocket 5 (Lift Key)": lambda state: state.has("Lift Key", player), + "Rocket Hideout B4F - Rocket 2 (Lift Key)": lambda state: state.has("Lift Key", player), + "Rocket Hideout B4F - Rocket 3 (Lift Key)": lambda state: state.has("Lift Key", player), - hidden_item_access_rules = { + # hidden items "Viridian Forest - Hidden Item Northwest by Trainer": lambda state: state.pokemon_rb_can_get_hidden_items( player), "Viridian Forest - Hidden Item Entrance Tree": lambda state: state.pokemon_rb_can_get_hidden_items(player), @@ -159,8 +174,6 @@ def set_rules(world, player): player), "Route 4 - Hidden Item Plateau East Of Mt Moon": lambda state: state.pokemon_rb_can_get_hidden_items(player), } - for loc, rule in access_rules.items(): - add_rule(world.get_location(loc, player), rule) - if world.randomize_hidden_items[player].value != 0: - for loc, rule in hidden_item_access_rules.items(): - add_rule(world.get_location(loc, player), rule) + for loc in world.get_locations(player): + if loc.name in access_rules: + add_rule(loc, access_rules[loc.name]) From 78d4da53a7f8490507171bbc1365019092b882fa Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Thu, 8 Dec 2022 02:06:34 +0100 Subject: [PATCH 24/35] Tests: verify and fix host.yaml/Utils.py match (#1302) --- Utils.py | 8 +++++--- test/general/TestHostYAML.py | 25 +++++++++++++++++++++++++ test/webhost/TestDocs.py | 5 +++-- test/webhost/TestFileGeneration.py | 7 ++++--- 4 files changed, 37 insertions(+), 8 deletions(-) create mode 100644 test/general/TestHostYAML.py diff --git a/Utils.py b/Utils.py index fdb86e63a8f5..4694864142aa 100644 --- a/Utils.py +++ b/Utils.py @@ -268,7 +268,6 @@ def get_default_options() -> OptionsType: "log_network": 0 }, "generator": { - "teams": 1, "enemizer_path": os.path.join("EnemizerCLI", "EnemizerCLI.Core"), "player_files_path": "Players", "players": 0, @@ -286,6 +285,7 @@ def get_default_options() -> OptionsType: }, "oot_options": { "rom_file": "The Legend of Zelda - Ocarina of Time.z64", + "rom_start": True }, "dkc3_options": { "rom_file": "Donkey Kong Country 3 - Dixie Kong's Double Trouble! (USA) (En,Fr).sfc", @@ -303,9 +303,11 @@ def get_default_options() -> OptionsType: "red_rom_file": "Pokemon Red (UE) [S][!].gb", "blue_rom_file": "Pokemon Blue (UE) [S][!].gb", "rom_start": True - } + }, + "ffr_options": { + "display_msgs": True, + }, } - return options diff --git a/test/general/TestHostYAML.py b/test/general/TestHostYAML.py new file mode 100644 index 000000000000..81ae9f8a677c --- /dev/null +++ b/test/general/TestHostYAML.py @@ -0,0 +1,25 @@ +import unittest + +import Utils + + +class TestIDs(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + with open(Utils.local_path("host.yaml")) as f: + cls.yaml_options = Utils.parse_yaml(f.read()) + + def testUtilsHasHost(self): + for option_key, option_set in Utils.get_default_options().items(): + with self.subTest(option_key): + self.assertIn(option_key, self.yaml_options) + for sub_option_key in option_set: + self.assertIn(sub_option_key, self.yaml_options[option_key]) + + def testHostHasUtils(self): + utils_options = Utils.get_default_options() + for option_key, option_set in self.yaml_options.items(): + with self.subTest(option_key): + self.assertIn(option_key, utils_options) + for sub_option_key in option_set: + self.assertIn(sub_option_key, utils_options[option_key]) diff --git a/test/webhost/TestDocs.py b/test/webhost/TestDocs.py index 6922deb122e9..f6ede1543e26 100644 --- a/test/webhost/TestDocs.py +++ b/test/webhost/TestDocs.py @@ -7,8 +7,9 @@ class TestDocs(unittest.TestCase): - def setUp(self) -> None: - self.tutorials_data = WebHost.create_ordered_tutorials_file() + @classmethod + def setUpClass(cls) -> None: + cls.tutorials_data = WebHost.create_ordered_tutorials_file() def testHasTutorial(self): games_with_tutorial = set(entry["gameTitle"] for entry in self.tutorials_data) diff --git a/test/webhost/TestFileGeneration.py b/test/webhost/TestFileGeneration.py index 656206cdec56..2954946db3f9 100644 --- a/test/webhost/TestFileGeneration.py +++ b/test/webhost/TestFileGeneration.py @@ -7,10 +7,11 @@ class TestFileGeneration(unittest.TestCase): - def setUp(self) -> None: - self.correct_path = os.path.join(os.path.dirname(WebHost.__file__), "WebHostLib") + @classmethod + def setUpClass(cls) -> None: + cls.correct_path = os.path.join(os.path.dirname(WebHost.__file__), "WebHostLib") # should not create the folder *here* - self.incorrect_path = os.path.join(os.path.split(os.path.dirname(__file__))[0], "WebHostLib") + cls.incorrect_path = os.path.join(os.path.split(os.path.dirname(__file__))[0], "WebHostLib") def testOptions(self): WebHost.create_options_files() From f5adc7bdc50b374447164107552ec677052efe2d Mon Sep 17 00:00:00 2001 From: Jarno Date: Thu, 8 Dec 2022 02:57:49 +0100 Subject: [PATCH 25/35] docs: world api fixed link (#1299) --- docs/apworld specification.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/apworld specification.md b/docs/apworld specification.md index 9b37fd831f66..ac89a46e102c 100644 --- a/docs/apworld specification.md +++ b/docs/apworld specification.md @@ -2,7 +2,7 @@ Archipelago depends on worlds to provide game-specific details like items, locations and output generation. Those are located in the `worlds/` folder (source) or `/lib/worlds/` (when installed). -See [world api.md](world api.md) for details. +See [world api.md](world%20api.md) for details. apworld provides a way to package and ship a world that is not part of the main distribution by placing a `*.apworld` file into the worlds folder. From 92f75f3e030feed8d29ff2f96e7cf40c06a2d25b Mon Sep 17 00:00:00 2001 From: PoryGone <98504756+PoryGone@users.noreply.github.com> Date: Thu, 8 Dec 2022 04:47:39 -0500 Subject: [PATCH 26/35] SA2B: Add missing Whistle location (#1306) --- worlds/sa2b/Locations.py | 1 + worlds/sa2b/Names/LocationName.py | 1 + worlds/sa2b/Regions.py | 1 + worlds/sa2b/Rules.py | 4 ++++ worlds/sa2b/__init__.py | 2 +- 5 files changed, 8 insertions(+), 1 deletion(-) diff --git a/worlds/sa2b/Locations.py b/worlds/sa2b/Locations.py index 0ce14877ea19..f5831cd7ee47 100644 --- a/worlds/sa2b/Locations.py +++ b/worlds/sa2b/Locations.py @@ -467,6 +467,7 @@ class SA2BLocation(Location): LocationName.city_escape_hidden_4: 0xFF0760, LocationName.green_forest_hidden_4: 0xFF0764, + LocationName.mission_street_hidden_4: 0xFF0766, LocationName.city_escape_hidden_5: 0xFF0780, } diff --git a/worlds/sa2b/Names/LocationName.py b/worlds/sa2b/Names/LocationName.py index 0bb0dc6508e6..34826a1ccd4a 100644 --- a/worlds/sa2b/Names/LocationName.py +++ b/worlds/sa2b/Names/LocationName.py @@ -118,6 +118,7 @@ mission_street_hidden_1 = "Mission Street - Hidden 1" mission_street_hidden_2 = "Mission Street - Hidden 2" mission_street_hidden_3 = "Mission Street - Hidden 3" +mission_street_hidden_4 = "Mission Street - Hidden 4" mission_street_beetle = "Mission Street - Gold Beetle" mission_street_upgrade = "Mission Street - Upgrade" route_101_1 = "Route 101 - 1" diff --git a/worlds/sa2b/Regions.py b/worlds/sa2b/Regions.py index 42688153dd6c..8d3ceb91e292 100644 --- a/worlds/sa2b/Regions.py +++ b/worlds/sa2b/Regions.py @@ -269,6 +269,7 @@ def create_regions(world, player: int, active_locations): LocationName.mission_street_hidden_1, LocationName.mission_street_hidden_2, LocationName.mission_street_hidden_3, + LocationName.mission_street_hidden_4, LocationName.mission_street_beetle, LocationName.mission_street_upgrade, ] diff --git a/worlds/sa2b/Rules.py b/worlds/sa2b/Rules.py index 7b27b73a298f..a0590ba29cd9 100644 --- a/worlds/sa2b/Rules.py +++ b/worlds/sa2b/Rules.py @@ -604,6 +604,8 @@ def set_mission_upgrade_rules_standard(world: MultiWorld, player: int): if world.whistlesanity[player].value == 2 or world.whistlesanity[player].value == 3: add_rule(world.get_location(LocationName.mission_street_hidden_3, player), lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.mission_street_hidden_4, player), + lambda state: state.has(ItemName.tails_booster, player)) add_rule(world.get_location(LocationName.death_chamber_hidden_1, player), lambda state: state.has(ItemName.knuckles_shovel_claws, player) and state.has(ItemName.knuckles_hammer_gloves, player)) @@ -1044,6 +1046,8 @@ def set_mission_upgrade_rules_hard(world: MultiWorld, player: int): if world.whistlesanity[player].value == 2 or world.whistlesanity[player].value == 3: add_rule(world.get_location(LocationName.mission_street_hidden_3, player), lambda state: state.has(ItemName.tails_booster, player)) + add_rule(world.get_location(LocationName.mission_street_hidden_4, player), + lambda state: state.has(ItemName.tails_booster, player)) add_rule(world.get_location(LocationName.death_chamber_hidden_1, player), lambda state: state.has(ItemName.knuckles_shovel_claws, player) and state.has(ItemName.knuckles_hammer_gloves, player)) diff --git a/worlds/sa2b/__init__.py b/worlds/sa2b/__init__.py index 4e7443c24bfd..95e85ee36dbf 100644 --- a/worlds/sa2b/__init__.py +++ b/worlds/sa2b/__init__.py @@ -52,7 +52,7 @@ class SA2BWorld(World): game: str = "Sonic Adventure 2 Battle" option_definitions = sa2b_options topology_present = False - data_version = 3 + data_version = 4 item_name_groups = item_groups item_name_to_id = {name: data.code for name, data in item_table.items()} From f278dd95c572e70d410bacc36fbc3ad43aa266d3 Mon Sep 17 00:00:00 2001 From: Alchav <59858495+Alchav@users.noreply.github.com> Date: Thu, 8 Dec 2022 04:51:01 -0500 Subject: [PATCH 27/35] =?UTF-8?q?[Pok=C3=A9mon=20R/B]=20Major=20bug=20fix?= =?UTF-8?q?=20+=20dialogue=20change=20(#1305)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Pokemon R/B] Major bug fixes * [Pokemon R/B] Dialogue updates --- worlds/pokemon_rb/basepatch_blue.bsdiff4 | Bin 36544 -> 36517 bytes worlds/pokemon_rb/basepatch_red.bsdiff4 | Bin 36528 -> 36553 bytes worlds/pokemon_rb/locations.py | 2 ++ worlds/pokemon_rb/poke_data.py | 2 -- worlds/pokemon_rb/rom.py | 14 +++++++------- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/worlds/pokemon_rb/basepatch_blue.bsdiff4 b/worlds/pokemon_rb/basepatch_blue.bsdiff4 index dfffd8299b5bd3068e506e6007825b609fbd4807..9e98a2cfcdaa05387b1551a65048fab196d288bb 100644 GIT binary patch delta 36443 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 LML1B97!p!uSi>u5 delta 36416 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 literal 36528 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 Bh@Jod diff --git a/worlds/pokemon_rb/locations.py b/worlds/pokemon_rb/locations.py index 3e0148a80316..6b199690f9c7 100644 --- a/worlds/pokemon_rb/locations.py +++ b/worlds/pokemon_rb/locations.py @@ -28,6 +28,7 @@ def always_on(multiworld, player): return True + class LocationData: def __init__(self, region, name, original_item, rom_address=None, ram_address=None, event=False, type="Item", inclusion=always_on): @@ -42,6 +43,7 @@ def __init__(self, region, name, original_item, rom_address=None, ram_address=No self.type = type self.inclusion = inclusion + class EventFlag: def __init__(self, flag): self.byte = int(flag / 8) diff --git a/worlds/pokemon_rb/poke_data.py b/worlds/pokemon_rb/poke_data.py index 75222d570f8d..691db1c46eb8 100644 --- a/worlds/pokemon_rb/poke_data.py +++ b/worlds/pokemon_rb/poke_data.py @@ -1205,8 +1205,6 @@ 'Thunder Wave', 'Psywave', 'Explosion', 'Rock Slide', 'Tri Attack', 'Substitute' ] - - first_stage_pokemon = [pokemon for pokemon in pokemon_data.keys() if pokemon not in evolves_from] legendary_pokemon = ["Articuno", "Zapdos", "Moltres", "Mewtwo", "Mew"] diff --git a/worlds/pokemon_rb/rom.py b/worlds/pokemon_rb/rom.py index d209f0b295b6..a3e3510dff0e 100644 --- a/worlds/pokemon_rb/rom.py +++ b/worlds/pokemon_rb/rom.py @@ -40,7 +40,7 @@ def get_move(moves, chances, random, starting_move=False): def get_encounter_slots(self): - encounter_slots = [location for location in location_data if location.type == "Wild Encounter"] + encounter_slots = deepcopy([location for location in location_data if location.type == "Wild Encounter"]) for location in encounter_slots: if isinstance(location.original_item, list): @@ -107,11 +107,11 @@ def process_trainer_data(self, data): def process_static_pokemon(self): - starter_slots = [location for location in location_data if location.type == "Starter Pokemon"] - legendary_slots = [location for location in location_data if location.type == "Legendary Pokemon"] - static_slots = [location for location in location_data if location.type in - ["Static Pokemon", "Missable Pokemon"]] - legendary_mons = [slot.original_item for slot in legendary_slots] + starter_slots = deepcopy([location for location in location_data if location.type == "Starter Pokemon"]) + legendary_slots = deepcopy([location for location in location_data if location.type == "Legendary Pokemon"]) + static_slots = deepcopy([location for location in location_data if location.type in + ["Static Pokemon", "Missable Pokemon"]]) + legendary_mons = deepcopy([slot.original_item for slot in legendary_slots]) tower_6F_mons = set() for i in range(1, 11): @@ -224,7 +224,7 @@ def process_wild_pokemon(self): poke_data.pokemon_data[slot.original_item]["type2"] in [self.local_poke_data[mon]["type1"], self.local_poke_data[mon]["type2"]]])] if not candidate_locations: - candidate_locations = location_data + candidate_locations = get_encounter_slots(self) candidate_locations = [self.multiworld.get_location(location.name, self.player) for location in candidate_locations] candidate_locations.sort(key=lambda slot: abs(get_base_stat_total(slot.item.name) - stat_base)) for location in candidate_locations: From 1b582e5b09cbd80c1e5a22cb8be3eb3e86889a22 Mon Sep 17 00:00:00 2001 From: t3hf1gm3nt <59876300+t3hf1gm3nt@users.noreply.github.com> Date: Thu, 8 Dec 2022 04:52:52 -0500 Subject: [PATCH 28/35] docs: update pokemon and oot setup guides about changing lua options (#1303) - Pokemon guide was missing a point about changing the Lua option in Bizhawk. - The same point in the OoT guide was missing a step about restarting Bizhawk after changing the Lua option, so updated that as well. --- worlds/oot/docs/setup_en.md | 2 +- worlds/pokemon_rb/docs/setup_en.md | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/worlds/oot/docs/setup_en.md b/worlds/oot/docs/setup_en.md index d11cd8f38ceb..9c1aca78b92f 100644 --- a/worlds/oot/docs/setup_en.md +++ b/worlds/oot/docs/setup_en.md @@ -19,7 +19,7 @@ As we are using Bizhawk, this guide is only applicable to Windows and Linux syst Once Bizhawk has been installed, open Bizhawk and change the following settings: - Go to Config > Customize. Switch to the Advanced tab, then switch the Lua Core from "NLua+KopiLua" to - "Lua+LuaInterface". This is required for the Lua script to function correctly. + "Lua+LuaInterface". Then restart Bizhawk. This is required for the Lua script to function correctly. **NOTE: Even if "Lua+LuaInterface" is already selected, toggle between the two options and reselect it. Fresh installs** **of newer versions of Bizhawk have a tendency to show "Lua+LuaInterface" as the default selected option but still load** **"NLua+KopiLua" until this step is done.** diff --git a/worlds/pokemon_rb/docs/setup_en.md b/worlds/pokemon_rb/docs/setup_en.md index 5d83cb6598b0..3c54cf9008c6 100644 --- a/worlds/pokemon_rb/docs/setup_en.md +++ b/worlds/pokemon_rb/docs/setup_en.md @@ -23,6 +23,11 @@ As we are using Bizhawk, this guide is only applicable to Windows and Linux syst Once Bizhawk has been installed, open Bizhawk and change the following settings: +- Go to Config > Customize. Switch to the Advanced tab, then switch the Lua Core from "NLua+KopiLua" to + "Lua+LuaInterface". Then restart Bizhawk. This is required for the Lua script to function correctly. + **NOTE: Even if "Lua+LuaInterface" is already selected, toggle between the two options and reselect it. Fresh installs** + **of newer versions of Bizhawk have a tendency to show "Lua+LuaInterface" as the default selected option but still load** + **"NLua+KopiLua" until this step is done.** - Under Config > Customize > Advanced, make sure the box for AutoSaveRAM is checked, and click the 5s button. This reduces the possibility of losing save data in emulator crashes. - Under Config > Customize, check the "Run in background" box. This will prevent disconnecting from the client while From bedc78d3353de929f79461cf982db9c7d1f91960 Mon Sep 17 00:00:00 2001 From: Zach Parks Date: Thu, 8 Dec 2022 08:54:49 -0600 Subject: [PATCH 29/35] Rogue Legacy: Remove relative imports and move to .apworld. (#1304) * Remove relative import and remove `set_rule` usage. * Set Rogue Legacy to be .apworld. --- setup.py | 18 ++++++++++-------- worlds/rogue_legacy/Rules.py | 10 ++++------ worlds/rogue_legacy/__init__.py | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/setup.py b/setup.py index fa739c00ade9..e60753d2dae1 100644 --- a/setup.py +++ b/setup.py @@ -1,23 +1,25 @@ +import base64 +import datetime import os +import platform import shutil import sys import sysconfig -import platform +import typing import zipfile -from pathlib import Path -from hashlib import sha3_512 -import base64 -import datetime -from Utils import version_tuple, is_windows, is_linux from collections.abc import Iterable -import typing +from hashlib import sha3_512 +from pathlib import Path + import setuptools -from Launcher import components, icon_paths +from Launcher import components, icon_paths +from Utils import version_tuple, is_windows, is_linux apworlds: set = { "Subnautica", "Factorio", + "Rogue Legacy", } # This is a bit jank. We need cx-Freeze to be able to run anything from this script, so install it diff --git a/worlds/rogue_legacy/Rules.py b/worlds/rogue_legacy/Rules.py index 92b9ba0a6cce..4ddf24c745f6 100644 --- a/worlds/rogue_legacy/Rules.py +++ b/worlds/rogue_legacy/Rules.py @@ -1,7 +1,5 @@ from BaseClasses import MultiWorld, CollectionState -from ..generic.Rules import set_rule - def get_upgrade_total(multiworld: MultiWorld, player: int) -> int: return int(multiworld.health_pool[player]) + int(multiworld.mana_pool[player]) + \ @@ -52,8 +50,8 @@ def has_defeated_dungeon(state: CollectionState, player: int) -> bool: def set_rules(multiworld: MultiWorld, player: int): # If 'vendors' are 'normal', then expect it to show up in the first half(ish) of the spheres. if multiworld.vendors[player] == "normal": - set_rule(multiworld.get_location("Forest Abkhazia Boss Reward", player), - lambda state: has_vendors(state, player)) + multiworld.get_location("Forest Abkhazia Boss Reward", player).access_rule = \ + lambda state: has_vendors(state, player) # Gate each manor location so everything isn't dumped into sphere 1. manor_rules = { @@ -92,11 +90,11 @@ def set_rules(multiworld: MultiWorld, player: int): # Set rules for manor locations. for event, locations in manor_rules.items(): for location in locations: - set_rule(multiworld.get_location(location, player), lambda state: state.has(event, player)) + multiworld.get_location(location, player).access_rule = lambda state: state.has(event, player) # Set rules for fairy chests to decrease headache of expectation to find non-movement fairy chests. for fairy_location in [location for location in multiworld.get_locations(player) if "Fairy" in location.name]: - set_rule(fairy_location, lambda state: has_fairy_progression(state, player)) + fairy_location.access_rule = lambda state: has_fairy_progression(state, player) # Region rules. multiworld.get_entrance("Forest Abkhazia", player).access_rule = \ diff --git a/worlds/rogue_legacy/__init__.py b/worlds/rogue_legacy/__init__.py index 34f7c785ba5f..9b6535b6c117 100644 --- a/worlds/rogue_legacy/__init__.py +++ b/worlds/rogue_legacy/__init__.py @@ -1,12 +1,12 @@ from typing import List from BaseClasses import Tutorial +from worlds.AutoWorld import World, WebWorld from .Items import RLItem, RLItemData, event_item_table, item_table, get_items_by_category from .Locations import RLLocation, location_table from .Options import rl_options from .Regions import create_regions from .Rules import set_rules -from ..AutoWorld import World, WebWorld class RLWeb(WebWorld): From 79bb43b77cb7b3222506049986db0a7bc6127207 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Thu, 8 Dec 2022 21:23:31 +0100 Subject: [PATCH 30/35] Core: embed custom datapackage into .archipelago (#1288) Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com> --- Main.py | 12 ++++++++++-- MultiServer.py | 12 +++++++++++- docs/network protocol.md | 13 ++++++++----- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/Main.py b/Main.py index 31351fc5f5e4..aaf192617d3d 100644 --- a/Main.py +++ b/Main.py @@ -11,7 +11,7 @@ from typing import Dict, List, Tuple, Optional, Set from BaseClasses import Item, MultiWorld, CollectionState, Region, RegionType, LocationProgressType, Location -from worlds.alttp.Items import item_name_groups +import worlds from worlds.alttp.Regions import is_main_entrance from Fill import distribute_items_restrictive, flood_items, balance_multiworld_progression, distribute_planned from worlds.alttp.Shops import SHOP_ID_START, total_shop_slots, FillDisabledShopSlots @@ -368,6 +368,13 @@ def precollect_hint(location): for player in world.groups.get(location.item.player, {}).get("players", [])]): precollect_hint(location) + # custom datapackage + datapackage = {} + for game_world in world.worlds.values(): + if game_world.data_version == 0 and game_world.game not in datapackage: + datapackage[game_world.game] = worlds.network_data_package["games"][game_world.game] + datapackage[game_world.game]["item_name_groups"] = game_world.item_name_groups + multidata = { "slot_data": slot_data, "slot_info": slot_info, @@ -387,7 +394,8 @@ def precollect_hint(location): "version": tuple(version_tuple), "tags": ["AP"], "minimum_versions": minimum_versions, - "seed_name": world.seed_name + "seed_name": world.seed_name, + "datapackage": datapackage, } AutoWorld.call_all(world, "modify_multidata", multidata) diff --git a/MultiServer.py b/MultiServer.py index 072e5aa1e69a..be53119fe617 100644 --- a/MultiServer.py +++ b/MultiServer.py @@ -124,6 +124,7 @@ class Context: stored_data_notification_clients: typing.Dict[str, typing.Set[Client]] item_names: typing.Dict[int, str] = Utils.KeyedDefaultDict(lambda code: f'Unknown item (ID:{code})') + item_name_groups: typing.Dict[str, typing.Dict[str, typing.Set[str]]] location_names: typing.Dict[int, str] = Utils.KeyedDefaultDict(lambda code: f'Unknown location (ID:{code})') all_item_and_group_names: typing.Dict[str, typing.Set[str]] forced_auto_forfeits: typing.Dict[str, bool] @@ -202,7 +203,6 @@ def __init__(self, host: str, port: int, server_password: str, password: str, lo self.non_hintable_names = collections.defaultdict(frozenset) self._load_game_data() - self._init_game_data() # Datapackage retrieval def _load_game_data(self): @@ -412,6 +412,16 @@ def _load(self, decoded_obj: dict, use_embedded_server_options: bool): server_options = decoded_obj.get("server_options", {}) self._set_options(server_options) + # custom datapackage + for game_name, data in decoded_obj.get("datapackage", {}).items(): + logging.info(f"Loading custom datapackage for game {game_name}") + self.gamespackage[game_name] = data + self.item_name_groups[game_name] = data["item_name_groups"] + del data["item_name_groups"] # remove from datapackage, but keep in self.item_name_groups + self._init_game_data() + for game_name, data in self.item_name_groups.items(): + self.read_data[f"item_name_groups_{game_name}"] = lambda lgame=game_name: self.item_name_groups[lgame] + # saving def save(self, now=False) -> bool: diff --git a/docs/network protocol.md b/docs/network protocol.md index 74e3fab102b2..5b574a0de737 100644 --- a/docs/network protocol.md +++ b/docs/network protocol.md @@ -367,16 +367,19 @@ Used to request a single or multiple values from the server's data storage, see | keys | list\[str\] | Keys to retrieve the values for. | Additional arguments sent in this package will also be added to the [Retrieved](#Retrieved) package it triggers. -Some special keys exist with specific return data: -| Name | Type | Notes | -|----------------------------|-----------------------|----------------------------------------------| -| \_read_hints_{team}_{slot} | list\[[Hint](#Hint)\] | All Hints belonging to the requested Player. | -| \_read_slot_data_{slot} | any | slot_data belonging to the requested slot. | +Some special keys exist with specific return data, all of them have the prefix `_read_`, so `hints_{team}_{slot}` is `_read_hints_{team}_{slot}`. + +| Name | Type | Notes | +|-------------------------------|------------------------|---------------------------------------------------| +| hints_{team}_{slot} | list\[[Hint](#Hint)\] | All Hints belonging to the requested Player. | +| slot_data_{slot} | any | slot_data belonging to the requested slot. | +| item_name_groups_{game_name} | dict\[str, list\[str]] | item_name_groups belonging to the requested game. | ### Set Used to write data to the server's data storage, that data can then be shared across worlds or just saved for later. Values for keys in the data storage can be retrieved with a [Get](#Get) package, or monitored with a [SetNotify](#SetNotify) package. +Keys that start with `_read_` cannot be set. #### Arguments | Name | Type | Notes | |------------|-------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------| From c3fe341736a1e5de302fdc408ace6ef3bf2cef45 Mon Sep 17 00:00:00 2001 From: Jarno Date: Fri, 9 Dec 2022 10:24:08 +0100 Subject: [PATCH 31/35] Docs: slot_data typing (#1300) * Docs: slot_data typing * Properly escaped brackets [ ] --- docs/network protocol.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/network protocol.md b/docs/network protocol.md index 5b574a0de737..09990c96c31d 100644 --- a/docs/network protocol.md +++ b/docs/network protocol.md @@ -128,7 +128,7 @@ Sent to clients when the connection handshake is successfully completed. | players | list\[[NetworkPlayer](#NetworkPlayer)\] | List denoting other players in the multiworld, whether connected or not. | | missing_locations | list\[int\] | Contains ids of remaining locations that need to be checked. Useful for trackers, among other things. | | checked_locations | list\[int\] | Contains ids of all locations that have been checked. Useful for trackers, among other things. Location ids are in the range of ± 253-1. | -| slot_data | dict | Contains a json object for slot related data, differs per game. Empty if not required. Not present if slot_data in [Connect](#Connect) is false. | +| slot_data | dict\[str, any\] | Contains a json object for slot related data, differs per game. Empty if not required. Not present if slot_data in [Connect](#Connect) is false. | | slot_info | dict\[int, [NetworkSlot](#NetworkSlot)\] | maps each slot to a [NetworkSlot](#NetworkSlot) information | ### ReceivedItems @@ -370,12 +370,11 @@ Additional arguments sent in this package will also be added to the [Retrieved]( Some special keys exist with specific return data, all of them have the prefix `_read_`, so `hints_{team}_{slot}` is `_read_hints_{team}_{slot}`. -| Name | Type | Notes | -|-------------------------------|------------------------|---------------------------------------------------| -| hints_{team}_{slot} | list\[[Hint](#Hint)\] | All Hints belonging to the requested Player. | -| slot_data_{slot} | any | slot_data belonging to the requested slot. | -| item_name_groups_{game_name} | dict\[str, list\[str]] | item_name_groups belonging to the requested game. | - +| Name | Type | Notes | +|-------------------------------|--------------------------|---------------------------------------------------| +| hints_{team}_{slot} | list\[[Hint](#Hint)\] | All Hints belonging to the requested Player. | +| slot_data_{slot} | dict\[str, any\] | slot_data belonging to the requested slot. | +| item_name_groups_{game_name} | dict\[str, list\[str\]\] | item_name_groups belonging to the requested game. | ### Set Used to write data to the server's data storage, that data can then be shared across worlds or just saved for later. Values for keys in the data storage can be retrieved with a [Get](#Get) package, or monitored with a [SetNotify](#SetNotify) package. From b7d46004e211b28100edb914f685f23cc9c201e9 Mon Sep 17 00:00:00 2001 From: beauxq Date: Fri, 9 Dec 2022 17:54:29 -0800 Subject: [PATCH 32/35] Zillion: fix invalid slot data from race condition --- ZillionClient.py | 4 ++++ worlds/AutoWorld.py | 5 ++++- worlds/zillion/__init__.py | 6 ++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/ZillionClient.py b/ZillionClient.py index d518f5ef7607..1ace87748961 100644 --- a/ZillionClient.py +++ b/ZillionClient.py @@ -258,6 +258,10 @@ def on_package(self, cmd: str, args: Dict[str, Any]) -> None: assert id_ in id_to_loc self.loc_mem_to_id[mem] = id_ + if len(self.loc_mem_to_id) != 394: + logger.warn("invalid Zillion `Connected` packet, " + f"`slot_data` missing locations in `loc_mem_to_id` - len {len(self.loc_mem_to_id)}") + self.got_slot_data.set() payload = { diff --git a/worlds/AutoWorld.py b/worlds/AutoWorld.py index b8329b714f33..6837d7874922 100644 --- a/worlds/AutoWorld.py +++ b/worlds/AutoWorld.py @@ -251,7 +251,10 @@ def generate_output(self, output_directory: str) -> None: def fill_slot_data(self) -> Dict[str, Any]: # json of WebHostLib.models.Slot """Fill in the `slot_data` field in the `Connected` network package. This is a way the generator can give custom data to the client. - The client will receive this as JSON in the `Connected` response.""" + The client will receive this as JSON in the `Connected` response. + + The generation does not wait for `generate_output` to complete before calling this. + `threading.Event` can be used if you need to wait for something from `generate_output`.""" return {} def extend_hint_information(self, hint_data: Dict[int, Dict[int, str]]): diff --git a/worlds/zillion/__init__.py b/worlds/zillion/__init__.py index 0b9b0e51400c..069b156c961b 100644 --- a/worlds/zillion/__init__.py +++ b/worlds/zillion/__init__.py @@ -1,6 +1,7 @@ from collections import deque, Counter from contextlib import redirect_stdout import functools +import threading from typing import Any, Dict, List, Set, Tuple, Optional, cast import os import logging @@ -101,12 +102,15 @@ def flush(self) -> None: """ my_locations: List[ZillionLocation] = [] """ This is kind of a cache to avoid iterating through all the multiworld locations in logic. """ + slot_data_ready: threading.Event + """ This event is set in `generate_output` when the data is ready for `fill_slot_data` """ def __init__(self, world: MultiWorld, player: int): super().__init__(world, player) self.logger = logging.getLogger("Zillion") self.lsi = ZillionWorld.LogStreamInterface(self.logger) self.zz_system = System() + self.slot_data_ready = threading.Event() def _make_item_maps(self, start_char: Chars) -> None: _id_to_name, _id_to_zz_id, id_to_zz_item = make_id_to_others(start_char) @@ -338,6 +342,7 @@ def finalize_item_locations(self) -> None: zz_patcher.write_locations(self.zz_system.randomizer.regions, zz_options.start_char, self.zz_system.randomizer.loc_name_2_pretty) + self.slot_data_ready.set() zz_patcher.all_fixes_and_options(zz_options) zz_patcher.set_external_item_interface(zz_options.start_char, zz_options.max_level) zz_patcher.set_multiworld_items(multi_items) @@ -385,6 +390,7 @@ def fill_slot_data(self) -> Dict[str, Any]: # json of WebHostLib.models.Slot assert self.zz_system.randomizer, "didn't get randomizer from generate_early" rescues: Dict[str, Any] = {} + self.slot_data_ready.wait() for i in (0, 1): if i in zz_patcher.rescue_locations: ri = zz_patcher.rescue_locations[i] From 78a18dee4e258716e7a90bc2e86f7e6e28deeb6d Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sat, 10 Dec 2022 18:00:18 +0100 Subject: [PATCH 33/35] Subnautica: give Early Seaglide a display name (#1313) --- worlds/subnautica/Options.py | 1 + 1 file changed, 1 insertion(+) diff --git a/worlds/subnautica/Options.py b/worlds/subnautica/Options.py index 8984fa86746c..deef33d625ab 100644 --- a/worlds/subnautica/Options.py +++ b/worlds/subnautica/Options.py @@ -31,6 +31,7 @@ def consider_items(self) -> bool: class EarlySeaglide(DefaultOnToggle): + display_name = "Early Seaglide" """Make sure 2 of the Seaglide Fragments are available in or near the Safe Shallows (Sphere 1 Locations).""" From ce42fda85f07594ee3fbf647b56d36f4a2f0570b Mon Sep 17 00:00:00 2001 From: Zach Parks Date: Sat, 10 Dec 2022 17:24:05 -0600 Subject: [PATCH 34/35] Rogue Legacy: Fix early vendors and architect not being added to pool. (#1314) --- worlds/rogue_legacy/__init__.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/worlds/rogue_legacy/__init__.py b/worlds/rogue_legacy/__init__.py index 9b6535b6c117..68a0c856c8ad 100644 --- a/worlds/rogue_legacy/__init__.py +++ b/worlds/rogue_legacy/__init__.py @@ -1,8 +1,8 @@ from typing import List from BaseClasses import Tutorial -from worlds.AutoWorld import World, WebWorld -from .Items import RLItem, RLItemData, event_item_table, item_table, get_items_by_category +from worlds.AutoWorld import WebWorld, World +from .Items import RLItem, RLItemData, event_item_table, get_items_by_category, item_table from .Locations import RLLocation, location_table from .Options import rl_options from .Regions import create_regions @@ -77,7 +77,6 @@ def create_items(self): continue if self.get_setting("architect") == "early": self.multiworld.local_early_items[self.player]["Architect"] = 1 - continue # Blacksmith and Enchantress if name == "Blacksmith" or name == "Enchantress": @@ -87,7 +86,6 @@ def create_items(self): if self.get_setting("vendors") == "early": self.multiworld.local_early_items[self.player]["Blacksmith"] = 1 self.multiworld.local_early_items[self.player]["Enchantress"] = 1 - continue # Haggling if name == "Haggling" and self.get_setting("disable_charon"): From 2cdd03f78624e07f2a943381500f93e8c3b5cb23 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sun, 11 Dec 2022 02:59:17 +0100 Subject: [PATCH 35/35] Network: implement 0.4 marked compatibility removals (#757) * world remote items handling * players list when connecting --- Main.py | 4 ---- MultiServer.py | 31 ++++++----------------------- WebHostLib/api/__init__.py | 3 ++- docs/network protocol.md | 1 - docs/world api.md | 35 +++++++++------------------------ worlds/AutoWorld.py | 12 ----------- worlds/__init__.py | 2 -- worlds/alttp/Rom.py | 12 +++++------ worlds/alttp/__init__.py | 2 -- worlds/dark_souls_3/__init__.py | 2 -- worlds/ff1/__init__.py | 2 -- worlds/hylics2/__init__.py | 2 -- worlds/oot/__init__.py | 2 -- worlds/overcooked2/__init__.py | 2 -- worlds/pokemon_rb/__init__.py | 3 ++- worlds/sm/__init__.py | 3 --- worlds/smz3/__init__.py | 7 ++----- worlds/soe/__init__.py | 1 - worlds/timespinner/__init__.py | 1 - worlds/zillion/__init__.py | 8 -------- 20 files changed, 27 insertions(+), 108 deletions(-) diff --git a/Main.py b/Main.py index aaf192617d3d..f059f335b06e 100644 --- a/Main.py +++ b/Main.py @@ -381,10 +381,6 @@ def precollect_hint(location): "names": names, # TODO: remove around 0.2.5 in favor of slot_info "games": games, # TODO: remove around 0.2.5 in favor of slot_info "connect_names": {name: (0, player) for player, name in world.player_name.items()}, - "remote_items": {player for player in world.player_ids if - world.worlds[player].remote_items}, - "remote_start_inventory": {player for player in world.player_ids if - world.worlds[player].remote_start_inventory}, "locations": locations_data, "checks_in_area": checks_in_area, "server_options": baked_server_options, diff --git a/MultiServer.py b/MultiServer.py index be53119fe617..ca3522813249 100644 --- a/MultiServer.py +++ b/MultiServer.py @@ -148,8 +148,6 @@ def __init__(self, host: str, port: int, server_password: str, password: str, lo self.player_name_lookup: typing.Dict[str, team_slot] = {} self.connect_names = {} # names of slots clients can connect to self.allow_forfeits = {} - self.remote_items = set() - self.remote_start_inventory = set() # player location_id item_id target_player_id self.locations = {} self.host = host @@ -366,8 +364,6 @@ def _load(self, decoded_obj: dict, use_embedded_server_options: bool): self.seed_name = decoded_obj["seed_name"] self.random.seed(self.seed_name) self.connect_names = decoded_obj['connect_names'] - self.remote_items = decoded_obj['remote_items'] - self.remote_start_inventory = decoded_obj.get('remote_start_inventory', decoded_obj['remote_items']) self.locations = decoded_obj['locations'] self.slot_data = decoded_obj['slot_data'] for slot, data in self.slot_data.items(): @@ -548,7 +544,7 @@ def set_save(self, savedata: dict): if "stored_data" in savedata: self.stored_data = savedata["stored_data"] - # count items and slots from lists for item_handling = remote + # count items and slots from lists for items_handling = remote logging.info( f'Loaded save file with {sum([len(v) for k, v in self.received_items.items() if k[2]])} received items ' f'for {sum(k[2] for k in self.received_items)} players') @@ -708,10 +704,7 @@ async def on_client_connected(ctx: Context, client: Client): await ctx.send_msgs(client, [{ 'cmd': 'RoomInfo', 'password': bool(ctx.password), - # TODO remove around 0.4 - 'players': players, - # TODO convert to list of games present in 0.4 - 'games': [ctx.games[x] for x in range(1, len(ctx.games) + 1)], + 'games': {ctx.games[x] for x in range(1, len(ctx.games) + 1)}, # tags are for additional features in the communication. # Name them by feature or fork, as you feel is appropriate. 'tags': ctx.tags, @@ -719,8 +712,6 @@ async def on_client_connected(ctx: Context, client: Client): 'permissions': get_permissions(ctx), 'hint_cost': ctx.hint_cost, 'location_check_points': ctx.location_check_points, - 'datapackage_version': sum(game_data["version"] for game_data in ctx.gamespackage.values()) - if all(game_data["version"] for game_data in ctx.gamespackage.values()) else 0, 'datapackage_versions': {game: game_data["version"] for game, game_data in ctx.gamespackage.items()}, 'seed_name': ctx.seed_name, @@ -1557,20 +1548,10 @@ async def process_client_cmd(ctx: Context, client: Client, args: dict): minver = min_client_version if ignore_game else ctx.minimum_client_versions[slot] if minver > args['version']: errors.add('IncompatibleVersion') - if args.get('items_handling', None) is None: - # fall back to load from multidata - client.no_items = False - client.remote_items = slot in ctx.remote_items - client.remote_start_inventory = slot in ctx.remote_start_inventory - await ctx.send_msgs(client, [{ - "cmd": "Print", "text": - "Warning: Client is not sending items_handling flags, " - "which will not be supported in the future."}]) - else: - try: - client.items_handling = args['items_handling'] - except (ValueError, TypeError): - errors.add('InvalidItemsHandling') + try: + client.items_handling = args['items_handling'] + except (ValueError, TypeError): + errors.add('InvalidItemsHandling') # only exact version match allowed if ctx.compatibility == 0 and args['version'] != version_tuple: diff --git a/WebHostLib/api/__init__.py b/WebHostLib/api/__init__.py index bac25255c26f..eac19d84563b 100644 --- a/WebHostLib/api/__init__.py +++ b/WebHostLib/api/__init__.py @@ -39,10 +39,11 @@ def get_datapackage(): @api_endpoints.route('/datapackage_version') @cache.cached() + def get_datapackage_versions(): from worlds import network_data_package, AutoWorldRegister + version_package = {game: world.data_version for game, world in AutoWorldRegister.world_types.items()} - version_package["version"] = network_data_package["version"] return version_package diff --git a/docs/network protocol.md b/docs/network protocol.md index 09990c96c31d..6509c76def8c 100644 --- a/docs/network protocol.md +++ b/docs/network protocol.md @@ -74,7 +74,6 @@ Sent to clients when they connect to an Archipelago server. | hint_cost | int | The amount of points it costs to receive a hint from the server. | | location_check_points | int | The amount of hint points you receive per item/location check completed. || | games | list\[str\] | List of games present in this multiworld. | -| datapackage_version | int | Sum of individual games' datapackage version. Deprecated. Use `datapackage_versions` instead. | | datapackage_versions | dict\[str, int\] | Data versions of the individual games' data packages the server will send. Used to decide which games' caches are outdated. See [Data Package Contents](#Data-Package-Contents). | | seed_name | str | uniquely identifying name of this generation | | time | float | Unix time stamp of "now". Send for time synchronization if wanted for things like the DeathLink Bounce. | diff --git a/docs/world api.md b/docs/world api.md index d95627ddb324..9961ccb4e5f6 100644 --- a/docs/world api.md +++ b/docs/world api.md @@ -343,18 +343,6 @@ class MyGameWorld(World): option_definitions = mygame_options # assign the options dict to the world #... ``` - -### Local or Remote - -A world with `remote_items` set to `True` gets all items items from the server -and no item from the local game. So for an RPG opening a chest would not add -any item to your inventory, instead the server will send you what was in that -chest. The advantage is that a generic mod can be used that does not need to -know anything about the seed. - -A world with `remote_items` set to `False` will locally reward its local items. -For console games this can remove delay and make script/animation/dialog flow -more natural. These games typically have been edited to 'bake in' the items. ### A World Class Skeleton @@ -379,8 +367,6 @@ class MyGameWorld(World): game: str = "My Game" # name of the game/world option_definitions = mygame_options # options the player can set topology_present: bool = True # show path to required location checks in spoiler - remote_items: bool = False # True if all items come from the server - remote_start_inventory: bool = False # True if start inventory comes from the server # data_version is used to signal that items, locations or their names # changed. Set this to 0 during development so other games' clients do not @@ -415,17 +401,13 @@ The world has to provide the following things for generation * additions to the item pool * additions to the regions list: at least one called "Menu" * locations placed inside those regions -* a `def create_item(self, item: str) -> MyGameItem` for plando/manual placing -* applying `self.world.precollected_items` for plando/start inventory - if not using a `remote_start_inventory` +* a `def create_item(self, item: str) -> MyGameItem` to create any item on demand +* applying `self.world.push_precollected` for start inventory * a `def generate_output(self, output_directory: str)` that creates the output - if there is output to be generated. If only items are randomized and - `remote_items = True` it is possible to have a generic mod and output - generation can be skipped. In all other cases this is required. When this is - called, `self.world.get_locations()` has all locations for all players, with - properties `item` pointing to the item and `player` identifying the player. - `self.world.get_filled_locations(self.player)` will filter for this world. - `item.player` can be used to see if it's a local item. + files if there is output to be generated. When this is + called, `self.world.get_locations(self.player)` has all locations for the player, with + attribute `item` pointing to the item. + `location.item.player` can be used to see if it's a local item. In addition, the following methods can be implemented and attributes can be set @@ -433,12 +415,13 @@ In addition, the following methods can be implemented and attributes can be set called per player before any items or locations are created. You can set properties on your world here. Already has access to player options and RNG. * `def create_regions(self)` - called to place player's regions into the MultiWorld's regions list. If it's + called to place player's regions and their locations into the MultiWorld's regions list. If it's hard to separate, this can be done during `generate_early` or `basic` as well. * `def create_items(self)` called to place player's items into the MultiWorld's itempool. * `def set_rules(self)` - called to set access and item rules on locations and entrances. + called to set access and item rules on locations and entrances. + Locations have to be defined before this, or rule application can miss them. * `def generate_basic(self)` called after the previous steps. Some placement and player specific randomizations can be done here. After this step all regions and items have diff --git a/worlds/AutoWorld.py b/worlds/AutoWorld.py index 6837d7874922..9064684a2111 100644 --- a/worlds/AutoWorld.py +++ b/worlds/AutoWorld.py @@ -160,18 +160,6 @@ class World(metaclass=AutoWorldRegister): hint_blacklist: ClassVar[FrozenSet[str]] = frozenset() # any names that should not be hintable - # NOTE: remote_items and remote_start_inventory are now available in the network protocol for the client to set. - # These values will be removed. - # if a world is set to remote_items, then it just needs to send location checks to the server and the server - # sends back the items - # if a world is set to remote_items = False, then the server never sends an item where receiver == finder, - # the client finds its own items in its own world. - remote_items: bool = True - - # If remote_start_inventory is true, the start_inventory/world.precollected_items is sent on connection, - # otherwise the world implementation is in charge of writing the items to their output data. - remote_start_inventory: bool = True - # For games where after a victory it is impossible to go back in and get additional/remaining Locations checked. # this forces forfeit: auto for those games. forced_auto_forfeit: bool = False diff --git a/worlds/__init__.py b/worlds/__init__.py index a18b8e093cf0..f0a56d4b556d 100644 --- a/worlds/__init__.py +++ b/worlds/__init__.py @@ -81,13 +81,11 @@ class WorldSource(typing.NamedTuple): lookup_any_location_id_to_name.update(world.location_id_to_name) network_data_package: DataPackage = { - "version": sum(world.data_version for world in AutoWorldRegister.world_types.values()), "games": games, } # Set entire datapackage to version 0 if any of them are set to 0 if any(not world.data_version for world in AutoWorldRegister.world_types.values()): - network_data_package["version"] = 0 import logging logging.warning(f"Datapackage is in custom mode. Custom Worlds: " diff --git a/worlds/alttp/Rom.py b/worlds/alttp/Rom.py index 18e09ab19407..268cb9ebffb9 100644 --- a/worlds/alttp/Rom.py +++ b/worlds/alttp/Rom.py @@ -795,11 +795,11 @@ def patch_rom(world, rom, player, enemized): itemid = 0x33 elif location.item.compass: itemid = 0x25 - if world.worlds[player].remote_items: # remote items does not currently work - itemid = list(location_table.keys()).index(location.name) + 1 - assert itemid < 0x100 - rom.write_byte(location.player_address, 0xFF) - elif location.item.player != player: + # if world.worlds[player].remote_items: # remote items does not currently work + # itemid = list(location_table.keys()).index(location.name) + 1 + # assert itemid < 0x100 + # rom.write_byte(location.player_address, 0xFF) + if location.item.player != player: if location.player_address is not None: rom.write_byte(location.player_address, min(location.item.player, ROM_PLAYER_LIMIT)) else: @@ -1654,7 +1654,7 @@ def get_reveal_bytes(itemName): write_strings(rom, world, player) # remote items flag, does not currently work - rom.write_byte(0x18637C, int(world.worlds[player].remote_items)) + rom.write_byte(0x18637C, 0) # set rom name # 21 bytes diff --git a/worlds/alttp/__init__.py b/worlds/alttp/__init__.py index e2965b4315e7..5c899f4bbf9c 100644 --- a/worlds/alttp/__init__.py +++ b/worlds/alttp/__init__.py @@ -121,8 +121,6 @@ class ALTTPWorld(World): location_name_to_id = lookup_name_to_id data_version = 8 - remote_items: bool = False - remote_start_inventory: bool = False required_client_version = (0, 3, 2) web = ALTTPWeb() diff --git a/worlds/dark_souls_3/__init__.py b/worlds/dark_souls_3/__init__.py index b6979dfcb499..5239967037a7 100644 --- a/worlds/dark_souls_3/__init__.py +++ b/worlds/dark_souls_3/__init__.py @@ -51,8 +51,6 @@ class DarkSouls3World(World): game: str = "Dark Souls III" option_definitions = dark_souls_options topology_present: bool = True - remote_items: bool = False - remote_start_inventory: bool = False web = DarkSouls3Web() data_version = 4 base_id = 100000 diff --git a/worlds/ff1/__init__.py b/worlds/ff1/__init__.py index 5eeb3b1be9a1..08f534db43d1 100644 --- a/worlds/ff1/__init__.py +++ b/worlds/ff1/__init__.py @@ -30,9 +30,7 @@ class FF1World(World): option_definitions = ff1_options game = "Final Fantasy" topology_present = False - remote_items = True data_version = 2 - remote_start_inventory = True ff1_items = FF1Items() ff1_locations = FF1Locations() diff --git a/worlds/hylics2/__init__.py b/worlds/hylics2/__init__.py index 26a1a1131b35..f26af4beac00 100644 --- a/worlds/hylics2/__init__.py +++ b/worlds/hylics2/__init__.py @@ -36,8 +36,6 @@ class Hylics2World(World): option_definitions = Options.hylics2_options topology_present: bool = True - remote_items: bool = True - remote_start_inventory: bool = True data_version: 1 diff --git a/worlds/oot/__init__.py b/worlds/oot/__init__.py index 98136fc8cf41..f7a35026b07d 100644 --- a/worlds/oot/__init__.py +++ b/worlds/oot/__init__.py @@ -102,8 +102,6 @@ class OOTWorld(World): item_name_to_id = {item_name: oot_data_to_ap_id(data, False) for item_name, data in item_table.items() if data[2] is not None} location_name_to_id = location_name_to_id - remote_items: bool = False - remote_start_inventory: bool = False web = OOTWeb() data_version = 2 diff --git a/worlds/overcooked2/__init__.py b/worlds/overcooked2/__init__.py index 9b217e15366b..b58d8fccb0e5 100644 --- a/worlds/overcooked2/__init__.py +++ b/worlds/overcooked2/__init__.py @@ -49,8 +49,6 @@ class Overcooked2World(World): required_client_version = (0, 3, 4) option_definitions = overcooked_options topology_present: bool = False - remote_items: bool = True - remote_start_inventory: bool = False data_version = 2 item_name_to_id = item_name_to_id diff --git a/worlds/pokemon_rb/__init__.py b/worlds/pokemon_rb/__init__.py index 7d1984d197fc..8119f259813e 100644 --- a/worlds/pokemon_rb/__init__.py +++ b/worlds/pokemon_rb/__init__.py @@ -38,9 +38,10 @@ class PokemonRedBlueWorld(World): # -MuffinJets#4559 game = "Pokemon Red and Blue" option_definitions = pokemon_rb_options - remote_items = False + data_version = 3 required_client_version = (0, 3, 7) + topology_present = False diff --git a/worlds/sm/__init__.py b/worlds/sm/__init__.py index b7310be14417..c7f41092f596 100644 --- a/worlds/sm/__init__.py +++ b/worlds/sm/__init__.py @@ -93,9 +93,6 @@ class SMWorld(World): location_name_to_id = {key: locations_start_id + value.Id for key, value in locationsDict.items() if value.Id != None} web = SMWeb() - remote_items: bool = False - remote_start_inventory: bool = False - # changes to client DeathLink handling for 0.2.1 # changes to client Remote Item handling for 0.2.6 required_client_version = (0, 2, 6) diff --git a/worlds/smz3/__init__.py b/worlds/smz3/__init__.py index fdabc5c5967e..5e91b533b243 100644 --- a/worlds/smz3/__init__.py +++ b/worlds/smz3/__init__.py @@ -76,9 +76,6 @@ class SMZ3World(World): for key, value in TotalSMZ3World(Config(), "", 0, "").locationLookup.items()} web = SMZ3Web() - remote_items: bool = False - remote_start_inventory: bool = False - locationNamesGT: Set[str] = {loc.Name for loc in GanonsTower(None, None).Locations} # first added for 0.2.6 @@ -485,9 +482,9 @@ def remove(self, state: CollectionState, item: Item) -> bool: return False def create_item(self, name: str) -> Item: - return SMZ3Item(name, + return SMZ3Item(name, ItemClassification.progression if SMZ3World.isProgression(TotalSMZ3Item.ItemType[name]) else ItemClassification.filler, - TotalSMZ3Item.ItemType[name], self.item_name_to_id[name], + TotalSMZ3Item.ItemType[name], self.item_name_to_id[name], self.player, TotalSMZ3Item.Item(TotalSMZ3Item.ItemType[name], self)) diff --git a/worlds/soe/__init__.py b/worlds/soe/__init__.py index f45508bf4bd0..0eaf07ca30c0 100644 --- a/worlds/soe/__init__.py +++ b/worlds/soe/__init__.py @@ -153,7 +153,6 @@ class SoEWorld(World): game: str = "Secret of Evermore" option_definitions = soe_options topology_present = False - remote_items = False data_version = 4 web = SoEWebWorld() required_client_version = (0, 3, 5) diff --git a/worlds/timespinner/__init__.py b/worlds/timespinner/__init__.py index b93f10044f16..ffe89bf55756 100644 --- a/worlds/timespinner/__init__.py +++ b/worlds/timespinner/__init__.py @@ -43,7 +43,6 @@ class TimespinnerWorld(World): option_definitions = timespinner_options game = "Timespinner" topology_present = True - remote_items = False data_version = 10 web = TimespinnerWebWorld() diff --git a/worlds/zillion/__init__.py b/worlds/zillion/__init__.py index 069b156c961b..bc82c48002a5 100644 --- a/worlds/zillion/__init__.py +++ b/worlds/zillion/__init__.py @@ -61,14 +61,6 @@ class ZillionWorld(World): # retrieved by clients on every connection. data_version: int = 1 - # NOTE: remote_items and remote_start_inventory are now available in the network protocol for the client to set. - # These values will be removed. - # if a world is set to remote_items, then it just needs to send location checks to the server and the server - # sends back the items - # if a world is set to remote_items = False, then the server never sends an item where receiver == finder, - # the client finds its own items in its own world. - remote_items: bool = False - logger: logging.Logger class LogStreamInterface:

Finder